home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
mus
/
play
/
DES_Tracker2_00.lha
/
DTL
/
code
/
LScope.S
< prev
next >
Wrap
Text File
|
1993-12-17
|
100KB
|
3,196 lines
;;=======================================================================;;
;; L-Scope.S - Begun Tue, Mar 10, 1992. ;;
;; Last updated Dec 18, 1993 ;;
;;-----------------------------------------------------------------------;;
;; This is version 2.0 of the visual scope program ;;
;; for destracker.library rel 2.0+ (lib ver 3.0+) ;;
;;-----------------------------------------------------------------------;;
;; Designed and written by Darren E. Schebek ;;
;; Copyright ©1992,1993 by Darren E. Schebek ;;
;; All rights reserved. ;;
;; ;;
;; This source code is NOT public domain. ;;
;; ;;
;;=======================================================================;;
;; ;;
;; You can contact me at the following address by snail mail: ;;
;; ;;
;; Darren Schebek ;;
;; 5620 Sherwood Blvd. ;;
;; Delta, BC ;;
;; CANADA ;;
;; V4L-2C5 ;;
;; ;;
;; Or via usenet: dschebek@outb.wimsey.bc.ca ;;
;; ;;
;;=======================================================================;;
; Include files I need (V37 includes are required)...
Include "dp:system.gs" ;DevPac-specific (almost global) include.
INCLUDE "exec/memory.i"
INCLUDE "exec/interrupts.i"
INCLUDE "exec/execbase.i"
INCLUDE "graphics/gfxbase.i"
INCLUDE "hardware/intbits.i"
INCLUDE "hardware/custom.i"
INCLUDE "DTLib.I"
; Public variables required by cback.o ...
XDEF _main ;These six PUBLIC's are needed
XDEF _procname ;only because I'm linking with
XDEF _stack ;cback.o. Otherwise, they'd
XDEF _priority ;be chucked before you could say
XDEF _BackGroundIO ;"Use The Force, Luke!"
XDEF DOSBase
XDEF MemCleanup
XREF LScopePic
; Some structure definition support macros of my own...
LOADLIB MACRO
MOVE.L _\1Base,A6
ENDM
SYS MACRO
JSR _LVO\1(A6)
ENDM
BYTETAB MACRO ;Used for rerving space for a table of n bytes.
\1 EQU SOFFSET
SOFFSET SET SOFFSET+\2
ENDM
WORDTAB MACRO ;Used for rerving space for a table of n words.
\1 EQU SOFFSET
SOFFSET SET SOFFSET+(\2*2)
ENDM
APTRTAB MACRO ;Used for rerving space for a table of n pointers.
\1 EQU SOFFSET
SOFFSET SET SOFFSET+(\2*4)
ENDM
LONGTAB MACRO ;Used for rerving space for a table of n longwords.
\1 EQU SOFFSET
SOFFSET SET SOFFSET+(\2*4)
ENDM
_SYSBase = 4 ;_SysBase holds pointer to... well... SysBase.
NDOTS = 32 ;Number of pixels per scope. Also the scope's height.
; The ChannelScope structure...
STRUCTURE CHANNELSCOPE,0
APTR csc_ScreenBaseAddr ;Ptr to left center of channel's scope.
APTR csc_CurSamplePtr ;Ptr to current sample data position.
APTR csc_SampleEnd ;Ptr to end of sample data.
APTR csc_RepeatEnd ;Ptr to end of repeat part of sample.
ULONG csc_SampleStep ;Current step for sample index (based on period).
ULONG csc_RepeatDist ;Distance from end of sample to start of repeat part.
ULONG csc_ConsecRepDist ;Dist. from end of repeat part to start of repeat part.
UWORD csc_Frac ;Fractional portion of current sample index.
UWORD csc_Period ;Current period being played in channel.
UBYTE csc_Volume ;Current volume level of channel.
UBYTE csc_Pad ;Reserved byte, always 0.
APTR csc_AmplitudeTable ;Ptr to scanline offsets for scaling amplitudes.
WORDTAB csc_OldPositions,NDOTS ;Table of old pixel positions from previous frame.
BYTETAB csc_Amplitudes,NDOTS ;Table of amplitudes from current frame for averaging.
LABEL csc_SIZEOF
; The variables required by the program...
STRUCTURE LSCOPE,0
APTR IntuiBase ;Pointer to IntuitionBase.
APTR GFXBase ;Pointer to GfxBase.
APTR DosBase ;Pointer to DosBase.
APTR DTLBase ;Pointer to DTLBase.
APTR LScopeTaskPtr ;Pointer to LScope's TCB.
APTR MyWindow ;Pointer to my window structure.
APTR MyMessage ;Pointer to my message structure.
APTR MyMessagePort ;Pointer to my message port structure.
APTR VBIntAddr ;Pointer to my (VBlank) interrupt structure.
APTR PackedPicPtr ;Peter Piper picked a peck of packed pics. :)
APTR FileAddr ;More temp. storage for LoadDisplay.
ULONG FileSize ;And still more.
; VBlank variables ("old" below means one vblank old, which is really getting
; on in computer terms :).
APTR Plane0Addr ;Will hold pointer to plane 0 of display.
APTR Plane1Addr ;Will hold ptr to plane 1.
APTR DrawPlaneAddr ;Address of third plane that gets drawn to.
UWORD RestoreP0 ;Used to buffer background for peak meter code.
UWORD RestoreP1 ;Also used to buffer background.
APTRTAB OldInsts,4 ;Holds old insts used by each channel.
UBYTE LeftLevel ;Holds left output level display.
UBYTE RightLevel ;Holds right output level display.
BYTETAB OldLeftVol,2 ;Two bytes for old channel 0 & 3 peak volumes.
BYTETAB OldRightVol,2 ;Two bytes for old channel 1 & 2 peak volumes.
BYTETAB ChBarHeights,4 ;Height of each channel's volume bar.
BYTETAB ChOldVolumes,4 ;Holds channel volumes one VBlank old.
UBYTE NewNoteHits ;Bits 0 to 3 hold new note hits (Bit=1 means hit).
UBYTE DrawNAFlag ;Clear & no module loaded means clear value stats.
; The following variables are essentially copies of their DTLBase counterparts.
; They are compared with the correspnding DTL_Base values and if the two values
; don't match, the VBlank routine will update the display element for that
; value. This keeps the VBlank from having to redraw the entire display on
; every single vertical blank. Instead, the VBlank routine redraws only the
; display elements that have changed since the last VBlank...
UBYTE OldFlags ;Old contents of high byte of dtl_Flags.
UBYTE OldLFlags ;Old contents of low byte of dtl_Flags.
UBYTE OldGlobalVolume ;Old contents of dtl_GlobalVolume.
UBYTE OldCurPosition ;Old contents of dtl_CurPosition.
BYTE OldGlobalTempo ;Old contents of dtl_GlobalTempo (signed byte).
UBYTE OldCurrentTempo ;Old contents of dtl_CurrentTempo.
UBYTE OldStartPos ;Old contents of dtl_StartPosition.
UBYTE OldEndPos ;Old contents of dtl_EndPosition.
UBYTE OldCurNote ;Old contents of dtl_CurNoteIndex.
UBYTE OldStartNote ;Old contents of dtl_StartNoteIndex.
UBYTE OldEndNote ;Old contents of dtl_EndNoteIndex.
UBYTE OldNumIters ;Old contents of dtl_Iterations.
UBYTE OldItersToGo ;Old contents of dtl_IterationsToGo.
UBYTE OldStatus ;Old contents of low byte of dtl_Flags (again).
UBYTE OldJiffies ;Old number of elapsed jiffies.
UBYTE OldFineTempo ;Old contents of dtl_FineTempo.
ULONG OldCurHertz ;Old contents of dtl_CurrentHz.
UBYTE VBSigBit ;Used by VBlank to signal main() when VBlank freq. changes.
UBYTE RedrawSigBit ;Used by VBlank to signal main code to redraw display.
UBYTE CurVBFreq ;Current VBlank frequency in Hz.
UBYTE AlreadyBlankFlag ;Set when all channel scopes are "turned off".
UBYTE LeftOutput
UBYTE RightOutput
UBYTE LastCh0Output
UBYTE LastCh1Output
UBYTE LastCh2Output
UBYTE LastCh3Output
APTR BaseTablePtr ;Ptr to table of base frequencies based on USA/Euro platform.
APTR LeftScreenAddrP0 ;Ptr to left-center pixel position for left output scope, plane 0.
APTR RightScreenAddrP0 ;Ptr to left-center pixel position for right output scope, plane 0.
APTR LeftScreenAddrP1 ;Ptr to left-center pixel position for left output scope, plane 2.
APTR RightScreenAddrP1 ;Ptr to left-center pixel position for right output scope, plane 2.
STRUCT Channel0,csc_SIZEOF ;CHANNELSCOPE structure for audio channel #0.
STRUCT Channel1,csc_SIZEOF ;CHANNELSCOPE structure for audio channel #1.
STRUCT Channel2,csc_SIZEOF ;CHANNELSCOPE structure for audio channel #2.
STRUCT Channel3,csc_SIZEOF ;CHANNELSCOPE structure for audio channel #3.
APTRTAB ScaleTablePtrs,65 ;Table of ptrs to amplitude scaling tables.
WORDTAB OldLeftAmplitudes,NDOTS ;Holds pixel positions for previous frame (left output scope).
WORDTAB OldRightAmplitudes,NDOTS ;Holds pixel positions for previous frame (right output scope).
LONGTAB DotStepsTable,800 ;Table of sample steps, indexed with period.
WORDTAB PeriodsTable,800 ;Table of ((period*NDOTS)/2). Used to build DotStepsTable.
BYTETAB TablesBuffer,(NDOTS*65) ;Holds all tables pointed to by ScaleTablePtrs.
LABEL LS_SIZE ;Size of the L-Scope variable storage area.
; These are the flags used for my window...
MYIDCMP = MOUSEBUTTONS ;"I want mouse button events".
MYWFLAGS = BORDERLESS|BACKDROP ;Tell how to draw window.
; These are constants used in dealing with my low resolution bitmap display...
BPWIDTH = 320
BPHEIGHT = 200
BPBYTESPERROW = (BPWIDTH/8)
; These are screen bitmap offsets to various positions on the screen where
; things are drawn...
ENABLEOFFSET = (136*BPBYTESPERROW)+2 ;Leftmost channel enable "light".
MNAMEOFFSET = (200*BPBYTESPERROW)+7 ;1st char of module name.
LEFTOFFSET = (123*BPBYTESPERROW)+18 ;Leftmost "light" of left peak meter.
RIGHTOFFSET = (133*BPBYTESPERROW)+18 ;Leftmost "light" of right peak meter.
INAMEOFFSET = (63*BPBYTESPERROW)+17 ;1st char of channel #0 instrument name.
INAMEDISTANCE = (14*BPBYTESPERROW) ;Distance to next inst text field.
GVOLOFFSET = (147*BPBYTESPERROW)+10 ;1st char of global volume display.
GTEMPOOFFSET = (157*BPBYTESPERROW)+9 ;1st char of global tempo display.
FINETEMPOOFFSET = (165*BPBYTESPERROW)+9 ;1st char of global fine tempo display.
CURTEMPOOFFSET = (175*BPBYTESPERROW)+10 ;1st char of current tempo display.
HERTZOFFSET = (185*BPBYTESPERROW)+7 ;1st char of current hertz display.
CURPOSOFFSET = (147*BPBYTESPERROW)+23 ;1st char of current position display.
SPOSOFFSET = (158*BPBYTESPERROW)+23 ;1st char of start position display.
EPOSOFFSET = (166*BPBYTESPERROW)+23 ;1st char of end position display.
NITEROFFSET = (177*BPBYTESPERROW)+23 ;1st char of num of iterations display.
LITEROFFSET = (185*BPBYTESPERROW)+23 ;1st char of iterations left display.
CNOTEOFFSET = (147*BPBYTESPERROW)+36 ;1st char of current note display.
SNOTEOFFSET = (158*BPBYTESPERROW)+36 ;1st char of start note display.
ENOTEOFFSET = (166*BPBYTESPERROW)+36 ;1st char of end note display.
STATUSOFFSET = (185*BPBYTESPERROW)+27 ;1st char of status text display.
TIMEROFFSET = (200*BPBYTESPERROW)+28 ;1st char of elapsed time display.
COBASE = (61*(BPWIDTH/8))+2 ;Makes for less table-typing :).
BOTTOMCVROW = (63*BPBYTESPERROW)+COBASE ;Bottom of first channel volume bar.
; These are constants that define the pixel location on my screen of the
; upper left corner of the EXIT button (a bit kludgey, but hey, it's only an
; example program)...
EXITULX = 3
EXITULY = 3
EXITLRX = 11
EXITLRY = 11
_main
Start
; First, allocate space for program variables...
Move.L _SYSBase,A6
Move.L #LS_SIZE,D0
Move.L #MEMF_CLEAR,D1
SYS AllocMem
Tst.L D0 ;Allocate error?
Ble ProgAbort ;Yup, leave immediately.
Move.L D0,A4 ;Nobody's allowed to touch A4!
St VBSigBit(A4)
St RedrawSigBit(A4)
; Open intuition.library...
Lea IntName(PC),A1 ;Library name pointer in A1.
MoveQ #0,D0
SYS OpenLibrary ;Attempt to open library.
Move.L D0,IntuiBase(A4) ;Save pointer to library base.
Beq .Error ;Pointer = 0 means trouble!
; Open graphics.library...
Lea GfxName(PC),A1
MoveQ #0,D0
SYS OpenLibrary
Move.L D0,GFXBase(A4)
Beq .Error
; Open destracker.library v3.0+ (which opens dos.library for me)...
Lea DTLName(PC),A1
MoveQ #3,D0
SYS OpenLibrary
Move.L D0,DTLBase(A4)
Beq .Error
; Save pointer to DosBase...
Move.L D0,A0 ;Put DTLBase pointer in addr reg.
Move.L dtl_DosLib(A0),DosBase(A4) ;Copy DosBase pointer.
; Allocate a signal bit to use...
MoveQ #-1,D0
SYS AllocSignal
Move.B D0,VBSigBit(A4)
Bmi .Error
; Allocate another signal bit to use...
MoveQ #-1,D0
SYS AllocSignal
Move.B D0,RedrawSigBit(A4)
Bmi .Error
;------------------------------
; Initialization code follows. I should tell you now that just about every
; scrap of code in this program that deals with determining the current
; VBlank frequency is completely and utterly wrong. I've tried to determine
; how to do this correctly for all versions of the O.S. quickly, but to no
; avail. Not having any kind of developer status certainly doesn't help,
; either. Very, very frustrating. Anyways, the VBlankFrequency field in
; Execbase, contrary to what might seem obvious, does NOT hold the current
; VBlank frequency. I believe it always holds 60 on a North American Amiga,
; and always 50 on a European Amiga. Go figure out the thinking behind that
; one...
; Bump up my task priority...
LOADLIB SYS
Sub.L A1,A1
SYS FindTask
Move.L D0,LScopeTaskPtr(A4)
Move.L D0,A1
MoveQ #50,D0
SYS SetTaskPri
; Determine current VBlank frequency...
Bsr CalcVBlankFrequency
Move.B D0,CurVBFreq(A4)
; Determine platform type (N.American or European) and select the
; appropriate base constants table...
Lea BaseNAmerica,A0
Move.L GFXBase(A4),A1
Move.W gb_DisplayFlags(A1),D0
BTst #PAL,D0
Beq .StoreBase
Lea BaseEurope,A0
.StoreBase Move.L A0,BaseTablePtr(A4)
; Build the Lookup tables for scaling amplitudes based on channel volume...
Bsr BuildLUTables
Bsr BuildDotStepsTable
; Initialize each of the channel structures...
Lea Channel0(A4),A0
Bsr InitChannel
Lea Channel1(A4),A0
Bsr InitChannel
Lea Channel2(A4),A0
Bsr InitChannel
Lea Channel3(A4),A0
Bsr InitChannel
; I have to prime certain variables that are used by the VBlank. Since my
; VBlank routine will only update the display for these variables if they
; differ from their DTLBase counterparts, I store -1 in them so that when
; L-Scope is first run, the VBlank routine will automagically update the
; display for these variables...
MoveQ #0,D0
Move.B D0,LeftOutput(A4)
Move.B D0,RightOutput(A4)
Move.B D0,LeftLevel(A4)
Move.B D0,RightLevel(A4)
MoveQ #-1,D0
Move.B D0,OldStartPos(A4)
Move.B D0,OldEndPos(A4)
Move.B D0,OldStartNote(A4)
Move.B D0,OldEndNote(A4)
Move.B D0,OldCurNote(A4)
Move.B D0,OldGlobalVolume(A4)
Move.B D0,OldCurPosition(A4)
Move.B D0,OldGlobalTempo(A4)
Move.B D0,OldNumIters(A4)
Move.B D0,OldItersToGo(A4)
Move.B D0,OldCurrentTempo(A4)
Move.B D0,OldStatus(A4)
Move.B D0,OldJiffies(A4)
Move.B D0,OldFineTempo(A4)
Move.L D0,OldCurHertz(A4)
; Now, since the DTLBase note-hit flags could be anything when L-Scope is first
; run (even if no song is playing), I copy them directly to my OldFlags variable
; to make sure that L-Scope doesn't think that any note-hits just occurred...
Move.L DTLBase(A4),A6 ;Get ptr to DTL_Base.
Move.B dtl_Flags(A6),OldFlags(A4) ;Update note hit flags.
; Now open my own screen. My newscreen structure tells OpenScreen to initially
; place my screen at the bottom of the display. I do this so that I can set up
; the screen's display and palette while the screen is out of view, then use
; MoveScreen to pop it up to the top of the display. This way I avoid unpleasant
; wipes and flicker while creating my display and setting the palette...
Move.L IntuitionBase(A4),A6
Lea MyNewScreen(PC),A0 ;Ptr to my newscreen structure in A0.
SYS OpenScreen
Move.L D0,NWScreen ;Store screen ptr in my newwindow struct.
Beq .Error ;Couldn't open screen.
; Now open my window, which fills the entire screen except for the screen's
; title bar (So you can still drag the screen up and down)...
Lea MyNewWindow(PC),A0 ;Ptr to my newwindow structure in A0.
SYS OpenWindow
Move.L D0,MyWindow(A4) ;Save pointer to my window structure.
Beq .Error ;oops, something went wrong, so abort.
; Now set up my message port. I use the newly created window's UserPort...
Move.L D0,A0 ;Window pointer in A0.
Move.L wd_UserPort(A0),MyMessagePort(A4)
; Now, to reduce the amount of indirect addressing I'll need to do later on
; (and speed up the vblank code a bit), I extract the pointers to the bitplanes
; into which I do all rendering from my screen's bitmap structure. The
; screen is made up of three bitplanes. Bitplanes 0 and 1 contain the actual
; picture, and bitplane 2 is mostly blank (and is the bitplane into which most
; rendering is done, independently of the other two planes)...
Move.L NWScreen,A0 ;Fetch pointer to my screen structure.
Lea sc_BitMap(A0),A1
Move.L bm_Planes(A1),Plane0Addr(A4)
Move.L bm_Planes+4(A1),Plane1Addr(A4)
Move.L bm_Planes+8(A1),DrawPlaneAddr(A4)
; Set the screen base addresses for each of the six oscilloscopes...
Move.L bm_Planes+8(A1),D0
Add.L #(BPBYTESPERROW*30)+9,D0
Move.L D0,Channel0+csc_ScreenBaseAddr(A4)
AddQ.L #5,D0
Move.L D0,Channel3+csc_ScreenBaseAddr(A4)
AddQ.L #8,D0
Move.L D0,Channel1+csc_ScreenBaseAddr(A4)
AddQ.L #5,D0
Move.L D0,Channel2+csc_ScreenBaseAddr(A4)
; The left and right output scopes are rendered on two planes to get their
; yellow color, so I have two screen base address variables for each of them...
Move.L bm_Planes(A1),D0
Add.L #(BPBYTESPERROW*30)+3,D0
Move.L D0,LeftScreenAddrP0(A4)
Add.L #30,D0
Move.L D0,RightScreenAddrP0(A4)
Move.L bm_Planes+8(A1),D0
Add.L #(BPBYTESPERROW*30)+3,D0
Move.L D0,LeftScreenAddrP1(A4)
Add.L #30,D0
Move.L D0,RightScreenAddrP1(A4)
; Call UnpackDisplay to unpack the L-Scope display data to my screen's
; bitmap memory...
Move.L NWScreen,A0 ;Fetch pointer to my screen structure.
Bsr UnpackDisplay
; Save a small portion of the display that will likely be trashed...
Move.L Plane0Addr(A4),A1
Move.W LEFTOFFSET(A1),RestoreP0(A4)
Move.L Plane1Addr(A4),A1
Move.W LEFTOFFSET(A1),RestoreP1(A4)
; Now I install my vertical blank routine. This is one of the last things I do
; because a lot of variables have to be initialized first. Once I install the
; VBlank routine, it starts going right away, and the variables it uses had
; better bloody well be initialized by then =u) ...
Bsr InstallVBlank
Tst D0
Bmi .Error ;Couldn't install VBlank routine. Abort.
; Now that my screen is all drawn and set up, I can "pop" it to the top of the
; display using MoveScreen...
Move.L IntuitionBase(A4),A6 ;MoveScreen is Intuition function.
Move.L NWScreen,A0 ;Pointer to my screen in A0.
SYS ScreenToFront
; Now prep the main loop by caching some important pointers...
Move.L _SYSBase,A6 ;SYSBase pointer in A6.
Move.L MyMessagePort(A4),A5 ;Cache message port addr in A5.
;===============================
; Now the main loop begins. In it, I basically wait for an input event (I will
; only receive mouse button events). If I get anything other than a SELECTUP
; event, I just reply to the message and loop back to wait for another event.
; If, however, I do get a SELECTUP event, I check the mouse X and Y coordinates
; reported in the message to see if the mouse was over the EXIT button on my
; display. If not, I reply to the message and loop back for another event. If
; so, I fall out of the main loop and exit.
;
; I also wait on two other signal bits. Both are used by the VBlank interrupt
; to signal the main code. One is used whenever the VBlank interrupt detects
; that the frequency of the VBlank interrupt has changed (the oscilloscope code
; needs to know the current VBlank frequency so it knows how many samples to
; display per VBlank frame). The other is used whenever the VBLank interrupt
; occurs, and it tells the main code to redraw the display.
MoveQ #0,D7
Move.B MP_SIGBIT(A5),D1 ;Wait for window message.
BSet D1,D7
Move.B VBSigBit(A4),D1 ;Wait also for signal from VBlank.
BSet D1,D7
Move.B RedrawSigBit(A4),D1 ;Wait for redraw signal as well.
BSet D1,D7
.MainLoop Move.L D7,D0 ;Specify signal bits to wait for.
SYS Wait ;*ZZZzzzz...*
Move.B RedrawSigBit(A4),D1
BTst D1,D0 ;Redraw the display?
Beq .NoRedraw ;Nope.
Bsr RedrawDisplay ;Yup, redraw the display.
.NoRedraw Move.B VBSigBit(A4),D1 ;*znork!* huh? wazzat?
BTst D1,D0 ;Oh, did VBlank signal me?
Beq .NoVBChange ;Nope.
Bsr BuildDotStepsTable ;Yup, VBlank frequency changed.
.NoVBChange Move.L A5,A0 ;Get message port address.
SYS GetMsg ;Fetcheth the message.
Tst.L D0 ;Did I get anything?
Beq.S .MainLoop ;Nope.
Move.L D0,A1 ;Yup, get pointer into an address reg.
Move.W im_Code(A1),D2 ;Fetch relevant info.
Move.W im_MouseX(A1),D3
Move.W im_MouseY(A1),D4
Move.L D0,A1 ;Message pointer to A1.
SYS ReplyMsg ;Reply right away.
Cmp.W #SELECTUP,D2 ;Left mouse button released?
Bne.S .MainLoop ;Nope. Wait for another message.
Cmp.W #EXITULX,D3 ;Mouse to left of exit button?
Blt.S .MainLoop ;Yup, missed.
Cmp.W #EXITULY,D4 ;Mouse above exit button?
Blt.S .MainLoop ;Yup.
Cmp.W #EXITLRX,D3 ;Mouse to the right of exit button?
Bgt.S .MainLoop ;Yup. No go.
Cmp.W #EXITLRY,D4 ;Mouse below exit button?
Bgt.S .MainLoop ;Yup. Missed.
;------------------------------
; User clicked on the exit button, so deallocate everything and leave...
; Relieve the vertical blank routine from active duty...
.Exit Bsr RemoveVBlank
; Free up the VBlank signal bit...
.Error LOADLIB SYS
MoveQ #0,D0
Move.B VBSigBit(A4),D0
Bmi .RDSigBit
SYS FreeSignal
.RDSigBit MoveQ #0,D0
Move.B RedrawSigBit(A4),D0
Bmi .NoSigBit
SYS FreeSignal
; Close my window...
.NoSigBit Move.L IntuiBase(A4),A6
Move.L MyWindow(A4),D0
Beq .1
Move.L D0,A0
SYS CloseWindow
; Close my screen...
.1 Move.L IntuiBase(A4),A6
Move.L NWScreen,D0
Beq .2
Move.L D0,A0
SYS CloseScreen
; Close the destracker.library...
.2 Move.L _SYSBase,A6
Move.L DTLBase(A4),D0
Beq .3
Move.L D0,A1
SYS CloseLibrary
; Close the graphics.library...
.3 Move.L GFXBase(A4),D0
Beq .4
Move.L D0,A1
SYS CloseLibrary
; Close the intuition.library...
.4 Move.L IntuiBase(A4),D0
Beq .5
Move.L D0,A1
SYS CloseLibrary
; Free up the memory used by my variables...
.5 Move.L A4,A1
Move.L #LS_SIZE,D0
SYS FreeMem
; Dat's it! We be gone...
MemCleanup MoveQ #0,D0 ;Success return code in D0.
Leave Rts ;Exit program.
ProgAbort MoveQ #10,D0 ;Error return code in D0.
Bra.S Leave ;Exit program.
;;=======================;;
;; Subroutines follow... ;;
;;=======================;;
;---------------------------------------------------------------
; CalcVBlankFrequency routine. This function determines the frequency
; of the vertical blank for all versions of the operating system. Note
; that hardware hacks (like PAL-booters) are not supported. If you have
; a North American Amiga (NTSC) and you want to run with a PAL display, then
; upgrade to WB 2.0 or higher. Really. At this stage in the game, if you
; are still running WB 1.3 or lower, it's definitely time to upgrade.
;
; Input: Nothing.
;
; Output: D0.B = Current VBlank frequency in Hertz (?..255).
;
CalcVBlankFrequency
Move.L A6,-(Sp)
LOADLIB SYS
Cmp.B #36,LIB_VERSION(A6)
Bls.S .AD13
; Proper VBlank frequency calc for AmigaDOS v2.0 and up...
.AD20AndUp Move.L GFXBase(A4),A6
Move.L #(2000000000/280),D0
Move.W gb_current_tot_rows(A6),D1
MulU gb_current_tot_cclks(A6),D1
DivU D1,D0
AddQ.W #1,D0
Lsr.W #1,D0
Bra.S .Done
; Proper VBlank frequency calc for AmigaDOS v1.3 or lower...
.AD13 Move.B VBlankFrequency(A6),D0 ;Perhaps CBM made the same assumption. :)
.Done Move.L (Sp)+,A6
Rts
;---------------------------------------------------------------
; InitChannel routine. Initializes the specified CHANNELSCOPE structure.
;
; Input: A0 = Pointer to the channel structure.
;
; Output: Nothing.
;
InitChannel Move.L A2,-(Sp)
Move.L A0,A2
Lea BlankSample,A0
Lea 4(A0),A1
Move.L ScaleTablePtrs(A4),D1
MoveQ #0,D0
Move.L A0,csc_CurSamplePtr(A2)
Move.L A1,csc_SampleEnd(A2)
Move.L A1,csc_RepeatEnd(A2)
Move.L #4,csc_RepeatDist(A2)
Move.L #4,csc_ConsecRepDist(A2)
Move.L D0,csc_SampleStep(A2)
Move.W D0,csc_Period(A2)
Move.L D1,csc_AmplitudeTable(A2)
Move.B D0,csc_Volume(A2)
Move.L (Sp)+,A2
Rts
;---------------------------------------------------------------
; BuildDotStepsTable routine. This will recalculate the DotStepsTable
; based on VBlank frequency and platform (i.e. North American or European).
; It is called from the main code only: once during intialization, and
; then whenever the VBlank frequency changes (VBlank signals main code
; whenever this occurs)...
BuildDotStepsTable
Move.L D2,-(Sp)
Move.L BaseTablePtr(A4),A0
MoveQ #0,D0
Move.B CurVBFreq(A4),D0
Sub.W #30,D0
Bpl .1
MoveQ #30,D0
.1 Cmp.W #50,D0
Ble .2
MoveQ #50,D0
.2 Add.W D0,D0
Add.W D0,D0
Move.L 0(A0,D0.W),D1
Lea PeriodsTable(A4),A0
Lea DotStepsTable(A4),A1
Move.W #800-1,D0
.Loop Move.L D1,D2
DivU (A0)+,D2
Swap D2
Clr.W D2
Swap D2
Lsl.L #8,D2
Move.L D2,(A1)+
DBra D0,.Loop
Move.L (Sp)+,D2
Rts
;---------------------------------------------------------------
; BuildLUTables. This function builds the lookup tables required for
; the auto-scaling of amplitudes based on playback channel volume.
;
BuildLUTables MoveM.L D2-D4/A2/A3,-(Sp)
Lea ScaleTablePtrs(A4),A1
Lea TablesBuffer(A4),A0
Lea ScanlineSteps,A2
Lea ScanlineStarts,A3
MoveQ #65-1,D0 ;Build a table for each volume value.
.TableLoop Move.L A0,D2
Add.L #16,D2
Move.L D2,(A1)+
MoveQ #0,D1
Move.B (A3)+,D1
Ext.W D1
Swap D1
Move.L (A2)+,D2
MoveQ #NDOTS-1,D3 ;Each table is NDOTS (scope height) elements.
.ElementLoop Move.L D1,D4
Add.L #$8000,D4
Swap D4
Move.B D4,(A0)+
Add.L D2,D1
DBra D3,.ElementLoop
DBra D0,.TableLoop
; Now build the periods table...
Lea PeriodsTable(A4),A0
MoveQ #108,D1
Move.W #800-1,D0
.PerLoop Move.W D1,D2
MulU #NDOTS,D2
Lsr.L #1,D2
Move.W D2,(A0)+
AddQ #1,D1
DBra D0,.PerLoop
MoveM.L (Sp)+,D2-D4/A2/A3
Rts
;---------------------------------------------------------------
; UnpackIFFRow routine. Unpacks a single line of a single plane of a packed
; IFF ILBM. This routine was written by Colin Fox and he let me use it.
; Unfortunately, he didn't comment it. I optimized it a bit, and I commented
; that bit. :)
;
; Input: A0 = Ptr to variable holding address of BODY chunk (past ID & length).
; A1 = Ptr to destination bitplane line.
; D0 = Pixel width of destination line.
;
; Output: D0 = # of bytes written to destination.
;
UnpackIFFRow MoveM.L D2/D6/A2-A4,-(Sp)
Move.L A0,A2
Move.L A1,A3
Move.W D0,D6
AddQ.W #7,D6
Lsr.W #3,D6
Move.L (A2),A4
MoveQ #0,D2
.Check Cmp.W D2,D6
Ble.S .Exit
Move.B (A4)+,D0
Ext.W D0
Bmi.S .CheckRepeat
Add.W D0,D2
AddQ #1,D2
.StartCopy Move.B (A4)+,(A3)+
DBra D0,.StartCopy
Bra.S .Check
.CheckRepeat Neg.W D0 ;D0 is negative here. Negate it again.
Bmi.S .Check ;If still negative, then D0 = 128.
Add.W D0,D2
AddQ #1,D2
Move.B (A4)+,D1
.StartReplicate Move.B D1,(A3)+
DBra D0,.StartReplicate
Bra.S .Check
.Exit Move.L A4,(A2)
Move.L D6,D0
MoveM.L (Sp)+,D2/D6/A2-A4
Rts
;---------------------------------------------------------------
; UnpackDisplay routine. Unpacks the L-Scope display picture to the (at this
; point) newly created SCREEN's bitmap planes.
;
; Input: A0 = Pointer to the SCREEN structure.
; A4 = Pointer to L-Scope variables.
;
; Output: None.
;
UnpackDisplay MoveM.L D2-D3/A0-A3/A5-A6,-(Sp)
Move.L A0,A3 ;Put screen pointer in a safe place.
; Set the palette for the screen...
Lea sc_ViewPort(A3),A0 ;Ptr to screen's viewport in A0.
Lea PicPalette,A1 ;Ptr to my palette in A1.
MoveQ #8,D0 ;8 colors to load.
Move.L GFXBase(A4),A6
SYS LoadRGB4
; Now unpack the picture's planes to the screen's bitmap planes...
Move.L A3,A5
Lea LScopePic,A0
Move.L A0,PackedPicPtr(A4)
Lea PackedPicPtr(A4),A0
Lea sc_BitMap(A5),A1
Move.L bm_Planes+0(A1),A2
Move.L bm_Planes+4(A1),A3
Move.L bm_Planes+8(A1),A5
Lea BPBYTESPERROW*10(A2),A2 ;Skip past screen title (10 lines).
Lea BPBYTESPERROW*10(A3),A3 ;(Not terribly kosher, I know).
Lea BPBYTESPERROW*10(A5),A5
Move.W #200-1,D2 ;Loop to unpack 200 lines.
.UnpackLine Move.L A2,A1 ;A1 = Ptr to bitplane #0.
Move.W #BPWIDTH,D0 ;Unpack 320 pixels into plane.
Bsr UnpackIFFRow ;Do it.
Add.W D0,A1 ;Move to next line in plane.
Move.L A1,A2 ;Return updated ptr to A2.
Move.L A3,A1 ;Do same for plane #1.
Move.W #BPWIDTH,D0
Bsr UnpackIFFRow
Add.W D0,A1
Move.L A1,A3
Move.L A5,A1 ;Do same for plane #2.
Move.W #BPWIDTH,D0
Bsr UnpackIFFRow
Add.W D0,A1
Move.L A1,A5
DBra D2,.UnpackLine ;Unpack rest of picture data.
.Leave MoveM.L (Sp)+,D2-D3/A0-A3/A5-A6
Rts
;---------------------------------------------------------------
; InstallVBlank routine. Installs my own vertical blank routine using
; the Exec AddIntServer routine.
;
; Input: A4 = Pointer to L-Scope variables.
;
InstallVBlank Move.L _SYSBase,A6
; First, allocate an interrupt structure...
Move.L #IS_SIZE,D0 ;Size of the interrupt structure.
Move.L #MEMF_CLEAR|MEMF_PUBLIC,D1
SYS AllocMem
Move.L D0,VBIntAddr(A4) ;Save pointer to int struct.
Beq .AllocErr ;Hmmm. Error. Damn.
; Initialize various fields of the interrupt structure before adding it to
; the system's interrupt list...
Move.L D0,A1 ;Need int struct ptr in A1.
Move.B #NT_INTERRUPT,LN_TYPE(A1) ;"I am an interrupt".
Move.L #VBlankName,LN_NAME(A1) ;"My name is..."
Move.B #0,LN_PRI(A1) ;"I'm an average guy".
Move.L A4,IS_DATA(A1) ;VBlank needs ptr to L-Scope variables.
Lea LScopeVBlank,A0
Move.L A0,IS_CODE(A1) ;Ptr to vblank routine.
; Add my vblank interrupt to the system's vertical blank interrupt list...
Move.L #INTB_VERTB,D0
SYS AddIntServer
MoveQ #0,D0 ;Return success condition.
.Leave Rts
.AllocErr MoveQ #-1,D0 ;Return error condition.
Bra.S .Leave
;---------------------------------------------------------------
; RemoveVBlank routine. Relieves my vertical blank routine from active duty.
;
; Input: A4 = Pointer to L-Scope variables.
;
RemoveVBlank Move.L _SYSBase,A6
; First make sure that my vblank is currenty installed...
Tst.L VBIntAddr(A4)
Beq .Done
; Call Exec's RemInServer routine to rip my vblank routine right outa there...
Move.L VBIntAddr(A4),A1
Move.L #INTB_VERTB,D0
SYS RemIntServer
; Deallocate the memory used by my interrupt structure...
Move.L #IS_SIZE,D0
Move.L VBIntAddr(A4),A1
SYS FreeMem
.Done Rts
;------------------------------
; This macro is used for updating the current sample, playback period,
; volume, etc. for a specific CHANNELSCOPE structure.
UPDATECHANNEL MACRO
Lea Channel\1(A4),A0
Lea dtl_Channel\1(A6),A1
BTst #\1,dtl_Flags+1(A6) ;Is channel enabled?
Bne.S .Enabled\1 ;Yup, proceed.
Lea BlankSample,A3 ;Nope, no output for this channel.
Move.L A3,csc_CurSamplePtr(A0)
Lea 4(A3),A3
Move.L A3,csc_SampleEnd(A0)
Clr.L csc_SampleStep(A0)
Clr.W csc_Frac(A0)
Bra .Done\1
.Enabled\1 BTst #\1,NewNoteHits(A4) ;New note hit in Channel?
Beq.S .CheckPeriod\1 ;Nope.
; Set up new sample data...
Move.L cs_Instrument(A1),A3
Move.L is_Address(A3),D1
Move.L D1,csc_CurSamplePtr(A0)
Move.L D1,D2
Add.L is_Length(A3),D1
Move.L D1,csc_SampleEnd(A0)
Add.L is_Repeat(A3),D2
Add.L is_RepeatLen(A3),D2
Move.L D2,csc_RepeatEnd(A0)
Move.L is_Length(A3),D1
Sub.L is_Repeat(A3),D1
Move.L D1,csc_RepeatDist(A0)
MoveQ #0,D0
Move.W D0,csc_Frac(A0)
Move.L is_RepeatLen(A3),D1
Cmp.L #9,D1
Bgt.S .HasRep\1
MoveQ #0,D1
.HasRep\1 Move.L D1,csc_ConsecRepDist(A0)
; Check to see if a sample offset has been used...
Move.L cs_SampleOffset(A1),D0
Beq.S .CheckPeriod\1
Add.L D0,csc_CurSamplePtr(A0)
; Check to see if the period has changed...
.CheckPeriod\1 Move.W cs_Period(A1),D0
Cmp.W csc_Period(A0),D0
Beq.S .CheckVolume\1
Move.W D0,csc_Period(A0)
Sub.W #108,D0
Bpl.S .HavePer\1
MoveQ #0,D0
.HavePer\1 Add.W D0,D0
Add.W D0,D0
Move.L 0(A2,D0.W),csc_SampleStep(A0)
; Check to see if the channel volume has changed...
.CheckVolume\1 MoveQ #0,D0
Move.B cs_RealVolume(A1),D0
Cmp.B csc_Volume(A0),D0
Beq.S .Done\1
Move.B D0,csc_Volume(A0)
Add.W D0,D0
Add.W D0,D0
Move.L 0(A5,D0.W),csc_AmplitudeTable(A0)
.Done\1
ENDM
;------------------------------
; This macro handles the rendering of a single pixel for one of the
; four audio channels' oscilloscope display. It looks frighteningly
; CPU-intensive, considering it only draws one pixel, but it really does
; a lot more than that. It takes a raw sample element, scales it based on
; the current channel's volume, then converts that into a scanline offset.
; It also records the scanline position of the pixel for later averaging
; to produce the left/right total output data.
;
;
; Register setup for this macro invocation:
;
; D0 = Holds fractional sample index in low word.
; D1 = Holds fractional portion of dot-step in low word.
; D2 = Holds pointer to end of instrument sample (or end of repeat loop).
; D3 = Scratch. Used for rendering amplitude pixels.
; D4 = Unused.
; D5 = Holds integer portion of dot-step in low word.
; D6 = Pixel Offset. Determines which bit gets set in a screen memory byte.
; D7 = Scratch. Used for amplitude scaling and scanline computation.
; A0 = Pointer to WScope CHANNEL structure.
; A1 = Pointer to current instrument sample used by channel.
; A2 = Pointer to pixel screen offsets from previous frame for channel.
; A3 = Pointer to scanline offset lookup table.
; A4 = Pointer to channel's table for storing amplitudes for this frame.
; A5 = Pointer to base screen destination for rendering amplitudes.
; A6 = Pointer to amplitudes table to store for subsequent averaging.
;
DRAWCHDOT MACRO
Add.W D1,D0
Bcc.S .NoWrapYet\1
AddQ.W #1,A1
.NoWrapYet\1 Add.W D5,A1
Cmp.L D2,A1
Blo.S .NotEnd\1
; End of sample reached, loop back...
Sub.L csc_RepeatDist(A0),A1
Move.L csc_RepeatEnd(A0),D2
Move.L csc_ConsecRepDist(A0),csc_RepeatDist(A0)
Bne.S .NotEnd\1
; Control gets here if instrument is one-shot (i.e. no repeat loop)...
Lea BlankSample,A1
Move.L A1,D2
AddQ.L #4,D2
MoveQ #0,D1
; Get amplitude from sample., scale it according to volume, and convert it
; into a scanline offset for rendering the dot...
.NotEnd\1 MoveQ #0,D7
Move.B (A1),D7
Ext.W D7
Asr.W #3,D7 ;D7 now -16..+15
Move.B 0(A6,D7.W),D7 ;Get scanline #(-16..15) corrected for channel volume.
Move.B D7,(A4)+ ;Save for left/right meters.
Ext.W D7 ;Extend sign.
Add.W D7,D7 ;Make a word index out of it.
Move.W 0(A3,D7.W),D7 ;Get scanline offset.
; Erase this amplitude's dot at its old position
; and draw it at the new position...
Move.W (A2),D3
BClr D6,0(A5,D3.W)
BSet D6,0(A5,D7.W)
Move.W D7,(A2)+
; Next dot...
DBra D6,.NoWrap\1
AddQ.W #1,A5
MoveQ #8-1,D6
.NoWrap\1
ENDM
;------------------------------
; This macro is used by the code that creates the left/right total output
; oscilloscopes. It is invoked once for each pixel in each scope. Each time
; it is invoked, it takes output data from two audio channel scopes, averages
; them together and then renders a single pixel of the scope (left or right).
;
; Register setup for this macro invocation:
;
; D0 = $FFFE This is used as a mask for averaging calcs.
; D1 = Scratch Work register for averaging calcs.
; D2 = Scratch Work register for pixel rendering.
; D3 = Pixel Offset Determines which bit gets set in a screen memory byte.
; D4 = Unused
; D5 = Unused
; D6 = Unused
; D7 = Unused
; A0 = Pointer to 1st WScope CHANNEL structure's amplitudes table.
; A1 = Pointer to 2nd WScope CHANNEL structure's amplitudes table.
; A2 = Pointer to pixel screen offsets from previous frame for left/right channel.
; A3 = Pointer to base screen destination for rendering amplitudes.
; A4 = Pointer to WScope Global variables.
; A5 = Pointer to scanline offset lookup table.
; A6 = Pointer to base screen destination for second draw plane (to get yellow).
DRAWLRDOT MACRO
Move.B (A0)+,D1
Add.B (A1)+,D1
Ext.W D1
And.W D0,D1
Move.W 0(A5,D1.W),D1
; Erase this amplitude's dot at its old position and draw it at the new
; position. A little more involved than with the audio channel scopes
; because I have to erase/render for two planes instead of one...
Move.W (A2),D2
BClr D3,0(A3,D2.W)
BClr D3,0(A6,D2.W)
BSet D3,0(A3,D1.W)
BSet D3,0(A6,D1.W)
Move.W D1,(A2)+
; Next dot...
DBra D3,.Done\1
AddQ.W #1,A3
AddQ.W #1,A6
AddQ.W #8,D3
.Done\1
ENDM
;------------------------------
; RedrawDisplay routine. This routine is the guts of this whole program. It
; updates the display as fast as it can, and, in some cases, favors speed over
; memory (i.e., lots of table lookup, especially for decimal string conversion
; and text rendering). if you're a beginner or intermediate programmer, I hope
; you learn a thing or two from this code, since it uses a few programming
; optimization tricks, and 68000 tricks as well (but then again, it's far from
; being ideal code). Of course, like all programs, it does contain some code
; that you look at and frown. I'm very displeased with the way the peak meter
; rendering logic wound up. Ah well, maybe one day I'll clean it up...
RedrawDisplay MoveM.L D0-D7/A2-A6,-(Sp)
Move.L DTLBase(A4),A5 ;A5 points to ST library base.
Move.L DrawPlaneAddr(A4),A6 ;Cache draw plane pointer in A6.
MoveQ #0,D7 ;D7 always 0 (my impression of an 88100 :)
; Compute the new note-hits that occurred since last vblank and record them
; as set bits in NewNoteHits, with bits 0..3 corresponding to sound channels
; 0..3 ...
Move.B dtl_Flags(A5),D1 ;Get hi-byte of flags only.
And.B #$0F,D1 ;Only interested in note-hits.
Cmp.B OldFlags(A4),D1 ;Any new ones?
Bne .NewNotes ;Yup.
Move.B D7,NewNoteHits(A4) ;No new notes.
Bra .NoNewNotes
.NewNotes Move.B OldFlags(A4),D2 ;Compute new note bits.
Move.B D1,OldFlags(A4) ;Update OldFlags for next vblank.
Eor.B D2,D1 ;Isolate new note-hits in D1.
Move.B D1,NewNoteHits(A4) ;Store them for use later on.
.NoNewNotes
; Draw channel enable status "lights". These are the four "lights" at the
; middle of the left side of the screen. These "lights" are positioned on an
; EVEN byte boundary, so they can be rendered using word moves...
MoveQ #4-1,D0 ;Set up to loop for each channel.
Move.B dtl_Flags+1(A5),D1 ;Get channel enable flags in D1.
Lea ENABLEOFFSET(A6),A0 ;Get ptr to channel 0 "light" pos.
.DCE MoveQ #0,D2 ;Assume the channel is disabled.
Lsr.W #1,D1 ;Shift out channel enable status bit.
Bcc .DCE1 ;It was 0, so channel is disabled.
Move.W #$7FFC,D2 ;It was 1, so we draw the "light".
.DCE1 Move.W D2,BPBYTESPERROW(A0) ;Draw 2nd line of "light".
Move.W D2,(A0)+ ;Draw 1st line, move to next "light".
DBra D0,.DCE ;Loop for rest of sound channels.
; Update the channel volume bars. These are the four bars at the left side of
; the middle of the screen. Each bar is slightly less than one word (16 pixels)
; wide, and each is aligned on an even byte boundary, allowing me to use word
; moves to draw/erase them. Also, each bar is exactly 64 pixels in height.
; Here, I check each bar and decrement it's height by one if the bar's height
; is non-zero (just like the logic that decrements the pitch bars)...
MoveQ #4-1,D0 ;D0 = loop count and channel #.
Lea ChBarHeights+4(A4),A1 ;Get pointer to channel bar heights.
Lea ChannelOffsets(PC),A2 ;Get ptr to channel plane offsets.
MoveQ #0,D2 ;Make sure D2 is clear.
.UpdateChBars Move.B -(A1),D2 ;Get height of this channel bar.
Add.W D2,D2 ;Make word index out of bar height.
Move.W 0(A2,D2.W),A3 ;A3 holds offset to top lit line of bar.
Add.W D0,A3 ;Add in (X) offset to correct bar.
Add.W D0,A3 ;Again 'cause they're 2 bytes apart.
Add.L A6,A3 ;A3 now points to topmost lit line of bar.
Move.W D7,(A3) ;Erase highest lit line of bar.
Tst.B (A1) ;Is height currently zero?
Beq .NextBar ;Yup, don't decrement from it any more.
SubQ.B #1,(A1) ;Nope, reflect new bar height in table.
.NextBar DBra D0,.UpdateChBars ;Loop for all four channels.
; There are certain chores that this vblank routine needs do only while a song is
; currently playing. These chores follow...
BTst #DF_PLAYING,dtl_Flags+1(A5)
Bne .Playing
Tst.B AlreadyBlankFlag(A4)
Bne .NoPlay
; This hunk of code only gets called once when the current song stops
; playing. It looks gross, but it's faster than reloading an address
; register with the next channel structure's pointer...
St AlreadyBlankFlag(A4)
; "Turn off" all channel scopes by setting them to blank samples...
Lea BlankSample,A0
Lea 4(A0),A1
MoveQ #0,D0
MoveQ #64,D1
Move.L ScaleTablePtrs+256(A4),D2
MoveQ #4,D3
Move.B D0,NewNoteHits(A4)
Move.L A0,Channel0+csc_CurSamplePtr(A4)
Move.L A0,Channel1+csc_CurSamplePtr(A4)
Move.L A0,Channel2+csc_CurSamplePtr(A4)
Move.L A0,Channel3+csc_CurSamplePtr(A4)
Move.L A1,Channel0+csc_SampleEnd(A4)
Move.L A1,Channel1+csc_SampleEnd(A4)
Move.L A1,Channel2+csc_SampleEnd(A4)
Move.L A1,Channel3+csc_SampleEnd(A4)
Move.L D0,Channel0+csc_ConsecRepDist(A4)
Move.L D0,Channel1+csc_ConsecRepDist(A4)
Move.L D0,Channel2+csc_ConsecRepDist(A4)
Move.L D0,Channel3+csc_ConsecRepDist(A4)
Move.L D3,Channel0+csc_RepeatDist(A4)
Move.L D3,Channel1+csc_RepeatDist(A4)
Move.L D3,Channel2+csc_RepeatDist(A4)
Move.L D3,Channel3+csc_RepeatDist(A4)
Move.L D0,Channel0+csc_SampleStep(A4)
Move.L D0,Channel1+csc_SampleStep(A4)
Move.L D0,Channel2+csc_SampleStep(A4)
Move.L D0,Channel3+csc_SampleStep(A4)
Move.W D0,Channel0+csc_Period(A4)
Move.W D0,Channel1+csc_Period(A4)
Move.W D0,Channel2+csc_Period(A4)
Move.W D0,Channel3+csc_Period(A4)
Move.B D1,Channel0+csc_Volume(A4)
Move.B D1,Channel1+csc_Volume(A4)
Move.B D1,Channel2+csc_Volume(A4)
Move.B D1,Channel3+csc_Volume(A4)
Move.L D2,Channel0+csc_AmplitudeTable(A4)
Move.L D2,Channel1+csc_AmplitudeTable(A4)
Move.L D2,Channel2+csc_AmplitudeTable(A4)
Move.L D2,Channel3+csc_AmplitudeTable(A4)
; Render the scopes in their "turned off" state once only...
MoveM.L A5-A6,-(Sp)
Bsr DrawWaveforms
Bsr DrawLeftRight
MoveM.L (Sp)+,A5-A6
MoveQ #0,D7
Bra .NoPlay ;Skip past code for "playing a song" condition.
.Playing Move.B D7,AlreadyBlankFlag(A4)
MoveM.L A5-A6,-(Sp)
Lea DotStepsTable(A4),A2
Lea ScaleTablePtrs(A4),A5
Move.L DTLBase(A4),A6
; Update parameters for each of the four audio channel oscilloscopes...
UPDATECHANNEL 0
UPDATECHANNEL 1
UPDATECHANNEL 2
UPDATECHANNEL 3
; Render data for all six scopes for this frame...
Bsr DrawWaveforms
Bsr DrawLeftRight
MoveM.L (Sp)+,A5-A6
MoveQ #0,D7 ;Restore D7 since oscilloscope code trashes it.
; Here, I have to check for new note hits and update the appropriate channel's
; volume bar. This is a bit more complicated than the pitch bar logic, in that
; not only note-hits must be acted upon, but also any change in the channel's
; volume as well. Also, the bar is not simply filled, but instead is filled
; only to a height equal to the volume of the channel. None of this is done
; if the channel is disabled...
Lea ChBarHeights+3(A4),A1 ;A1 points to channel bar heights.
Lea ChOldVolumes+4(A4),A0 ;A0 points to old channel volumes.
Lea dtl_Channel3(A5),A2 ;We start with channel 3.
Move.B NewNoteHits(A4),D5 ;D5 holds new note hits.
MoveQ #4-1,D0 ;D0 = loop count and channel #.
.UpdateChVol BTst D0,dtl_Flags+1(A5) ;Is channel enabled?
Beq.S .NextChVol ;No, so don't update it.
Move.B cs_RealVolume(A2),D1 ;Get current volume of this channel.
Cmp.B -(A0),D1 ;Has volume changed since last time?
Bne .NewVol ;Yup, so we must redraw this bar.
BTst D0,D5 ;No, but was there a new note-hit?
Beq .NextChVol ;Nope. Go on to next channel.
; At this point, the bar must be redrawn, either because a new note-hit occurred,
; or the channel's volume was changed. I draw the bar up to a height equal
; to the channel's volume, and erase from there to the top of the bar. Even
; though the hardware allows volumes from 0..64 (65 values total), I prefer to
; deal in powers of two (programmers go absolutely orgasmic when they see a
; power of two)...
.NewVol Move.B cs_RealVolume(A2),D2 ;Get the new channel volume (0..64).
Move.B D2,(A0) ;Update old volume of this channel.
Cmp.B #63,D2 ;Is it greater than 63?
Bls.S .GoodVol ;Nope, so it's in range.
MoveQ #$3F,D2 ;Yup, clip to 0..63 range.
.GoodVol Move.B D2,(A1) ;Yup. ;Store new height of channel bar.
Lea BOTTOMCVROW(A6),A3 ;A3 points to bottom row.
Add.W D0,A3 ;Add in offset to proper channel bar.
Add.W D0,A3 ;Again 'cause they're 2 bytes apart.
MoveQ #64-1,D2 ;Loop for 64 lines of bar to render.
MoveQ #0,D3 ;D3 counts # of lines drawn in.
Move.W #$7FFC,D4 ;D4 holds data for drawing a bar line.
.DrawChBar Move.W D4,(A3) ;Draw one line of the channel bar.
Lea -BPBYTESPERROW(A3),A3 ;Move up to next line of bar.
Cmp.B (A0),D3 ;Drawn correct # of lines yet?
Beq .EraseChBar ;Yes, erase rest of bar to its top.
AddQ #1,D3 ;Nope, increment bar line #.
DBra D2,.DrawChBar ;And loop to draw another line.
Bra .NextChVol ;Bar is full, nothing to erase.
.EraseChBar Move.W D7,(A3) ;Erase a line of the bar.
Lea -BPBYTESPERROW(A3),A3 ;Move up to next bar line.
DBra D2,.EraseChBar ;Loop for rest of bar's height.
.NextChVol Lea -cs_SIZE(A2),A2 ;A2 points to next channel struct.
SubQ.W #1,A1 ;Next channel's bar height.
DBra D0,.UpdateChVol ;Loop for all four channels.
; Now update the module name field, located at the bottom-middle of the display.
; I erase the whole text field if there is no module currently loaded. If a
; module is loaded, then I draw in it's name. Funny, but for some reason I
; render the module name on every vblank. Isn't life odd sometimes?
.NoPlay Lea CharLookup(PC),A0 ;A0 holds ptr to char lookup table..
Lea MNAMEOFFSET(A6),A1 ;Get ptr to module name field pos.
Move.L dtl_ModuleStatus+ms_Name(A5),A2 ;A2 points to name string.
MoveQ #20-1,D2 ;Initial loop count (20 char cells).
Move.B dtl_Flags(A5),D0
Move.B OldLFlags(A4),D1
BTst #DF_MODULELOADED,dtl_Flags+1(A5)
Beq .EraseMName
; Render the module name string as fast as I can. The characters are each 5 lines
; in height, and are aligned on byte boundaries when drawn on screen. I don't use
; a loop so the code will execute faster without loop overhead. Also, I use
; "Move.B D7,..." instead of Clr.B because the move using a data register as its
; source operand is faster than a Clr.B by 4 cycles (Hey, it starts to add up
; after a while, ya know ;)...
Lea MNAMEOFFSET(A6),A1 ;Screen address to draw to.
.DMName MoveQ #0,D0 ;Clear out D0.
Move.B (A2)+,D0 ;Fetch a string character.
Beq .EraseMName ;Reached end of string. Erase rest.
Add.W D0,D0 ;Make longword index out of
Add.W D0,D0 ; ASCII value.
Move.L 0(A0,D0.W),A3 ;Use index to obtain char image ptr.
Move.B (A3)+,BPBYTESPERROW*4(A1) ;Draw 5th line of character.
Move.B (A3)+,BPBYTESPERROW*3(A1) ;Draw 4th line.
Move.B (A3)+,BPBYTESPERROW*2(A1) ;Draw 3rd line.
Move.B (A3)+,BPBYTESPERROW*1(A1) ;Draw 2nd line.
Move.B (A3),(A1)+ ;Draw 1st line and advance to next char cell.
SubQ #1,D2 ;One less char cell to be erased.
Bmi .DoneMName ;20 characters drawn, that's the limit.
Bra.S .DMName ;Draw chars until end of string reached.
; Erase remaining character cells...
.EraseMName Move.B D7,BPBYTESPERROW*4(A1) ;Erase 5th line of this char cell.
Move.B D7,BPBYTESPERROW*3(A1) ;Erase 4th line.
Move.B D7,BPBYTESPERROW*2(A1) ;Erase 3rd line.
Move.B D7,BPBYTESPERROW*1(A1) ;Erase 2nd line.
Move.B D7,(A1)+ ;Erase 1st line and advance to next char cell.
DBra D2,.EraseMName ;Loop for remaining character cells.
.DoneMName
; Now update the elapsed play timer. This only gets updated if the jiffies
; have changed since the last time I checked. I display elasped hours,
; minutes, seconds and jiffies...
MoveQ #0,D0
Move.B dtl_ElapsedJiffies(A5),D0
Cmp.B OldJiffies(A4),D0
Beq .DoneTimer ;Nope, don't update.
Lea CharLookup(PC),A0 ;Cache lookup table ptr in A0.
Lea TIMEROFFSET+9(A6),A1 ;Yup, get display offset.
Move.B D0,OldJiffies(A4) ;Update the jiffies variable.
Bsr Print2Digits
Move.B dtl_ElapsedSeconds(A5),D0
Lea TIMEROFFSET+6(A6),A1
Bsr Print2Digits
Move.B dtl_ElapsedMinutes(A5),D0
Lea TIMEROFFSET+3(A6),A1
Bsr Print2Digits
Move.B dtl_ElapsedHours(A5),D0
Lea TIMEROFFSET(A6),A1
Bsr Print2Digits
.DoneTimer
; Now I have to check each instrument name and draw or erase each text
; field as necessary in much the same way as I handled the module name text.
; However, here I have to draw in the instrument name, and then erase the rest
; of the text field to make sure that no characters from the old instrument
; name remain left behind. This routine uses lots of address registers...
MoveM.L A4/A5/A6,-(Sp) ;I need the extra registers, so...
MoveQ #4-1,D0 ;Loop once for each sound channel.
Lea dtl_Channel0(A5),A0 ;A0 points to first channel struct.
Lea OldInsts(A4),A1 ;A1 points to old channel inst ptrs table.
Lea INAMEOFFSET(A6),A2 ;Screen ptr to channel 0 inst name pos.
Lea CharLookup(PC),A4 ;Careful use of my sacred register!
Move.B dtl_Flags+1(A5),D3 ;Get copy of flags field.
.DoINames Move.L cs_Instrument(A0),A3 ;Get ptr to channel 0 instrument.
MoveQ #22-1,D1 ;Prepare for loop to draw 22 chars.
Move.L A2,A5 ;Keep original pointer in A2.
BTst D0,D3 ;Is this channel enabled?
Beq .EraseIName ;Nope.
Cmp.L (A1),A3 ;Has instrument changed?
Beq .NextIName ;Nope, move on to next channel.
Move.L A3,(A1) ;Update old channel inst pointer.
Beq .EraseIName ;A3 holds 0. No instrument is used.
Move.L is_Name(A3),A3 ;Extract inst name pointer to A3.
.DrawIName MoveQ #0,D2 ;Make sure D2 is clear.
Move.B (A3)+,D2 ;Get a char from inst name string.
Beq .EraseIName ;Reached end of string. Done.
Add.W D2,D2 ;Make a longword index from
Add.W D2,D2 ; the ASCII char value.
Move.L 0(A4,D2.W),A6 ;Get character image ptr in A6.
Move.B (A6)+,BPBYTESPERROW*4(A5) ;Draw 5th line of character.
Move.B (A6)+,BPBYTESPERROW*3(A5) ;Draw 4th line.
Move.B (A6)+,BPBYTESPERROW*2(A5) ;Draw 3rd line.
Move.B (A6)+,BPBYTESPERROW*1(A5) ;Draw 2nd line.
Move.B (A6),(A5)+ ;Draw first line and advance to next cell.
DBra D1,.DrawIName ;Loop for rest of string.
Bra .NextIName ;No cells left to erase. Next channel...
; Erase any remaining character cells if the instrument name was less than
; 22 characters in length...
.EraseIName Move.B D7,BPBYTESPERROW*4(A5) ;Erase 5th line of character cell.
Move.B D7,BPBYTESPERROW*3(A5) ;Erase 4th line.
Move.B D7,BPBYTESPERROW*2(A5) ;Erase 3rd line.
Move.B D7,BPBYTESPERROW*1(A5) ;Erase 2nd line.
Move.B D7,(A5)+ ;Erase 1st line and advance to next cell.
DBra D1,.EraseIName ;Loop for remaining character cells.
.NextIName AddQ #4,A1 ;Next old channel instrument ptr.
Lea cs_SIZE(A0),A0 ;Next sound channel.
Lea INAMEDISTANCE(A2),A2 ;Move to next inst text field area.
DBra D0,.DoINames ;Loop for all four sound channels.
.DoneINames MoveM.L (Sp)+,A4/A5/A6 ;Restore the registers I trashed.
; Hey, hey, now it's time to render the fake left/right peak meter displays.
; If the new left output is greater than the current value that I have, then
; I display the new left output level. Otherwise, I decrease the old level
; a bit. Then I do the same for the right output.
;
; The rendering code for the left/right peak meters is totally gross, IMHO.
MoveQ #0,D0
BTst #DF_PLAYING,dtl_Flags+1(A5) ;Is a song currently playing?
Beq .NoNewLeft ;No, no new left peak meter level.
Move.B LeftOutput(A4),D0 ;Get left output level (0..16).
Add.B D0,D0
Add.B D0,D0
Cmp.B LeftLevel(A4),D0
Blt.S .NoNewLeft
Move.B D0,LeftLevel(A4)
.NoNewLeft Move.B LeftLevel(A4),D0 ;Nope, don't change a thing.
; Now I render the left peak meter. WARNING: This code is REALLY ugly...
.NewLeftLevel AddQ #7,D0
MoveQ #8-1,D1 ;Loop for eight meter "lights".
MoveQ #0,D3
Move.L Plane0Addr(A4),A2
Move.L Plane1Addr(A4),A3
Lea LEFTOFFSET(A6),A0 ;Get ptr to first meter "light" pos.
Lea LEFTOFFSET(A2),A2
Lea LEFTOFFSET(A3),A3
Move.W #$7FFC,D2 ;Value used to render "lights".
Move.W #$8003,D4
Lsr.W #3,D0 ;Divide by 8 to get 0..7 (1..8 lights).
Beq .EraseLeft ;=0 means vol. too low to register.
.DrawLeft Cmp.W #7,D3
Blt .NotRedL
Or.W D2,BPBYTESPERROW(A2)
Or.W D2,(A2)
And.W D4,BPBYTESPERROW(A3)
And.W D4,(A3)
Bra .SkipYellowL
.NotRedL Cmp.W #6,D3
Blt .SkipYellowL
Or.W D2,BPBYTESPERROW(A2)
Or.W D2,(A2)
.SkipYellowL Move.W D2,BPBYTESPERROW(A0) ;Draw 2nd line of "light".
Move.W D2,(A0)+ ;Draw 1st and advance to next "light".
AddQ.W #2,A2
AddQ.W #2,A3
AddQ #1,D3
SubQ #1,D0 ;Decrement light counter.
Beq .EraseLeft ;"Turn off" remaining "lights".
DBra D1,.DrawLeft ;Else loop for "lights" that are on.
Bra .DoneLeft ;No "lights" left to be erased.
.EraseLeft Move.W RestoreP0(A4),D2
Move.W RestoreP1(A4),D3
Bra .DoErL
.EraseL Move.W D2,BPBYTESPERROW(A2)
Move.W D2,(A2)+
Move.W D3,BPBYTESPERROW(A3)
Move.W D3,(A3)+
Move.W D7,BPBYTESPERROW(A0) ;Erase 2nd line of "light".
Move.W D7,(A0)+ ;Erase 1st and advance to next "light".
.DoErL DBra D1,.EraseL ;Loop for remaining lights.
.DoneLeft SubQ.B #2,LeftLevel(A4) ;Reduce meter level a little at a time.
Bpl .DoRight ;There's still some volume left.
Clr.B LeftLevel(A4) ;Meter level wrapped, so clear it.
; Well, now we do EXACTLY the same thing for the right peak meter, except that
; we examine channels 0 and 3 instead of 1 and 2...
.DoRight MoveQ #0,D0
BTst #DF_PLAYING,dtl_Flags+1(A5) ;Is a song currently playing?
Beq .NoNewRight ;No, no new right peak meter level.
Move.B RightOutput(A4),D0 ;Get right output level (0..16).
Add.B D0,D0
Add.B D0,D0
Cmp.B RightLevel(A4),D0
Blt.S .NoNewRight
Move.B D0,RightLevel(A4)
.NoNewRight Move.B RightLevel(A4),D0 ;Nope, don't change a thing.
; Now I render the left peak meter. WARNING: This code is REALLY ugly...
.NewRightLevel AddQ #7,D0
MoveQ #8-1,D1 ;Loop for eight meter "lights".
MoveQ #0,D3
Move.L Plane0Addr(A4),A2
Move.L Plane1Addr(A4),A3
Lea RIGHTOFFSET(A6),A0 ;Get ptr to first meter "light" pos.
Lea RIGHTOFFSET(A2),A2
Lea RIGHTOFFSET(A3),A3
Move.W #$7FFC,D2 ;Value used to render "lights".
Move.W #$8003,D4
Lsr.W #3,D0 ;Divide by 2 to get 0..7 (1..8 lights).
Beq .EraseRight ;=0 means vol. too low to register.
.DrawRight Cmp.W #7,D3
Blt .NotRedR
Or.W D2,BPBYTESPERROW(A2)
Or.W D2,(A2)
And.W D4,BPBYTESPERROW(A3)
And.W D4,(A3)
Bra .SkipYellowR
.NotRedR Cmp.W #6,D3
Blt .SkipYellowR
Or.W D2,BPBYTESPERROW(A2)
Or.W D2,(A2)
.SkipYellowR Move.W D2,BPBYTESPERROW(A0) ;Draw 2nd line of "light".
Move.W D2,(A0)+ ;Draw 1st and advance to next "light".
AddQ.W #2,A2
AddQ #2,A3
AddQ #1,D3
SubQ #1,D0 ;Decrement light counter.
Beq .EraseRight ;"Turn off" remaining "lights".
DBra D1,.DrawRight ;Else loop for "lights" that are on.
Bra .DoneRight ;No "lights" left to be erased.
.EraseRight Move.W RestoreP0(A4),D2
Move.W RestoreP1(A4),D3
Bra .DoErR
.EraseR Move.W D2,BPBYTESPERROW(A2)
Move.W D2,(A2)+
Move.W D3,BPBYTESPERROW(A3)
Move.W D3,(A3)+
Move.W D7,BPBYTESPERROW(A0) ;Erase 2nd line of "light".
Move.W D7,(A0)+ ;Erase 1st and advance to next "light".
.DoErR DBra D1,.EraseR ;Loop for remaining lights.
.DoneRight SubQ.B #2,RightLevel(A4) ;Reduce meter level a little at a time.
Bpl .DoneRightBar ;There's still some volume left.
Clr.B RightLevel(A4) ;Meter level wrapped, so clear it.
.DoneRightBar
; Now it's time to update the numerical statistics. i.e. the values that must be
; converted to decimal strings and rendered to the display. I decided to use a
; subroutine to convert and print the values, even though it costs me 34 cycles
; for every BSR and RTS instruction pair (Grrr), but duplicating the conversion/
; print code for each value to be rendered is a bit much. I consoled myself by
; writing a PIG of a decimal string conversion routine (it uses three 256 byte
; lookup tables. YeeeHAAA!). But hell, is it fast. Kinda makes up for the
; urine-like peak-meter code. :)
Lea CharLookup(PC),A0 ;Cache lookup table ptr in A0.
MoveQ #0,D0 ;Clear out that register!
Move.B dtl_GlobalVolume(A5),D0 ;Get current global volume.
Cmp.B OldGlobalVolume(A4),D0 ;Has it changed since last time?
Beq .DoneGVolume ;Nope, don't update it then.
Move.B D0,OldGlobalVolume(A4) ;Yes, update variable.
Lea GVOLOFFSET(A6),A1 ;Load up screen dest for text.
Bsr PrintByte ;Convert value to string and print.
.DoneGVolume
; Dealing with the global tempo is a bit diffrerent, since it's a signed value.
; If the global tempo is negative, I manually print a "-" sign, then negate the
; value, making it positive, then convert and print that...
Move.B dtl_GlobalTempo(A5),D0 ;Get current global tempo.
Cmp.B OldGlobalTempo(A4),D0 ;Has it changed?
Beq .DoneGTempo ;Nope.
Lea GTEMPOOFFSET(A6),A1 ;Yup, get dest addr for text.
Move.B D7,BPBYTESPERROW*2(A1) ;Erase spot for "-" char.
Move.B D0,OldGlobalTempo(A4) ;Update old variable.
Bpl .PrintGT ;New tempo is positive.
Neg.B D0 ;Oop, it's negative, make it pos.
MoveQ #$7E,D1
Move.B D1,BPBYTESPERROW*2(A1) ; and draw in the "-" sign.
.PrintGT AddQ #1,A1 ;Advance to next char. cell.
Bsr PrintByte ;Convert value to string and print it.
.DoneGTempo
; Now do much the same thing for the global fine tempo...
Move.B dtl_FineTempo(A5),D0 ;Get current fine tempo.
Cmp.B OldFineTempo(A4),D0 ;Has it changed?
Beq .DoneFTempo ;Nope.
Lea FINETEMPOOFFSET(A6),A1 ;Yup, get dest addr for text.
Move.B D7,BPBYTESPERROW*2(A1) ;Erase spot for "-" char.
Move.B D0,OldFineTempo(A4) ;Update old variable.
Bpl .PrintFT ;New tempo is positive.
Neg.B D0 ;Oop, it's negative, make it pos.
MoveQ #$7E,D1
Move.B D1,BPBYTESPERROW*2(A1) ; and draw in the "-" sign.
.PrintFT AddQ #1,A1 ;Advance to next char. cell.
Bsr PrintByte ;Convert value to string and print it.
.DoneFTempo
; Now display the current hertz...
Move.L dtl_CurrentHertz(A5),D2 ;Get current playback freq.
Cmp.L OldCurHertz(A4),D2 ;Has it changed?
Beq .DoneCurrentHz ;Nope.
Move.L D2,OldCurHertz(A4) ;Update old variable.
Move.L D2,D0
Clr.W D0
Swap D0
And.W #$FF,D0
MulU #100,D2
Add.L #$8000,D2
Clr.W D2
Swap D2
Cmp.B #99,D2
Ble .FracHzOK
Sub.B #100,D2
AddQ #1,D0
.FracHzOK And.W #$FF,D2
Lea HERTZOFFSET(A6),A1 ;Yup, get dest addr for integer.
Bsr PrintByte ;Print integer part of Hz.
Move.L D2,D0
Lea HERTZOFFSET+4(A6),A1 ;Yup, get dest addr for fraction.
Bsr Print2Digits ;Print fractional part of Hz.
.DoneCurrentHz
Move.B dtl_Iterations(A5),D0 ;Get current # of iterations.
Cmp.B OldNumIters(A4),D0 ;Has it changed?
Beq .DoneNumIters ;Nope.
Move.B D0,OldNumIters(A4) ;Yup, update variable contents.
Lea NITEROFFSET(A6),A1 ;Get ptr to dest screen position.
Bsr PrintByte ;Convert and print new value.
.DoneNumIters
Move.B dtl_IterationsToGo(A5),D0 ;Get current # of iterations left.
Cmp.B OldItersToGo(A4),D0 ;Has it changed?
Beq .DoneItersToGo ;Nope.
Move.B D0,OldItersToGo(A4) ;Yup, update it for next time.
Lea LITEROFFSET(A6),A1 ;Get ptr to screen position.
Bsr PrintByte ;Convert and print the new value.
.DoneItersToGo
; The rest of the stats that require conversion to decimal strings are valid
; only when a module is loaded. If no module is loaded, then I write "---" in
; place of these stats, since they cannot have legitimate values. I even have
; a special subroutine just for printing "---" at a specified screen location :)
BTst #DF_MODULELOADED,dtl_Flags+1(A5) ;Is a module loaded?
Bne .IsAModule ;Yup, check for value updating.
Tst.B DrawNAFlag(A4) ;Have we already cleared stats?
Bne .DoneModStats ;Yup, once is enough.
St DrawNAFlag(A4) ;Set one-shot update flag.
Lea CURTEMPOOFFSET(A6),A1 ;Screen pos. for current tempo.
Bsr PrintNA ;Print "---".
Lea CURPOSOFFSET(A6),A1 ;Screen pos. for current position.
Bsr PrintNA
Lea SPOSOFFSET(A6),A1 ;Screen pos. for start position.
Bsr PrintNA
Lea EPOSOFFSET(A6),A1 ;Screen pos. for end position.
Bsr PrintNA
Lea CNOTEOFFSET(A6),A1 ;Screen pos. for current note index.
Bsr PrintNA
Lea SNOTEOFFSET(A6),A1 ;Screen pos. for start note index.
Bsr PrintNA
Lea ENOTEOFFSET(A6),A1 ;Screen pos. for end note index.
Bsr PrintNA
; Now I prime these stats so that as soon as a module is loaded, all of them
; will be instantly updated on screen (since they are then assumed to be valid
; because a module is now loaded, the vblank will not re-render them (eliminating
; the "---") unless the contents have changed). So...
MoveQ #-1,D0
Move.B D0,OldCurrentTempo(A4) ;Prime these stats for
Move.B D0,OldCurPosition(A4) ; when a new module is loaded.
Move.B D0,OldStartPos(A4)
Move.B D0,OldEndPos(A4)
Move.B D0,OldCurNote(A4)
Move.B D0,OldStartNote(A4)
Move.B D0,OldEndNote(A4)
Bra .DoneModStats ;Skip the proper update logic.
; The following bits of code are so repetitive that I couldn't bring myself
; to comment them. Essentially, each little code-unit checks to see if a given
; parameter has changed and re-renders it if it has...
.IsAModule Move.B D7,DrawNAFlag(A4)
Move.B dtl_CurrentTempo(A5),D0
Cmp.B OldCurrentTempo(A4),D0
Beq .DoneCurTempo
Move.B D0,OldCurrentTempo(A4)
Lea CURTEMPOOFFSET(A6),A1
Bsr PrintByte
.DoneCurTempo
Move.B dtl_CurPosition(A5),D0
Cmp.B OldCurPosition(A4),D0
Beq .DoneCPosition
Move.B D0,OldCurPosition(A4)
Lea CURPOSOFFSET(A6),A1
Bsr PrintByte
.DoneCPosition
Move.B dtl_StartPosition(A5),D0
Cmp.B OldStartPos(A4),D0
Beq .DoneStartPos
Move.B D0,OldStartPos(A4)
Lea SPOSOFFSET(A6),A1
Bsr PrintByte
.DoneStartPos
Move.B dtl_EndPosition(A5),D0
Cmp.B OldEndPos(A4),D0
Beq .DoneEndPos
Move.B D0,OldEndPos(A4)
Lea EPOSOFFSET(A6),A1
Bsr PrintByte
.DoneEndPos
Move.B dtl_CurNoteIndex(A5),D0
Cmp.B OldCurNote(A4),D0
Beq .DoneCurNote
Move.B D0,OldCurNote(A4)
Lea CNOTEOFFSET(A6),A1
Bsr PrintByte
.DoneCurNote
Move.B dtl_StartNoteIndex(A5),D0
Cmp.B OldStartNote(A4),D0
Beq .DoneStartNote
Move.B D0,OldStartNote(A4)
Lea SNOTEOFFSET(A6),A1
Bsr PrintByte
.DoneStartNote
Move.B dtl_EndNoteIndex(A5),D0
Cmp.B OldEndNote(A4),D0
Beq .DoneEndNote
Move.B D0,OldEndNote(A4)
Lea ENOTEOFFSET(A6),A1
Bsr PrintByte
.DoneEndNote
.DoneModStats
; Lastly update the status text field if necessary...
MoveQ #0,D0
Move.B dtl_PlayStatus(A5),D0
Cmp.B OldStatus(A4),D0
Beq .DoneStatus
Move.B D0,OldStatus(A4)
Add.W D0,D0
Add.W D0,D0
Lea StatusPtrs(PC),A0
Move.L 0(A0,D0.W),A0
Lea CharLookup(PC),A1
Lea STATUSOFFSET(A6),A2
.DrawStatus MoveQ #0,D1
Move.B (A0)+,D1
Beq .DoneStatus
Add.W D1,D1
Add.W D1,D1
Move.L 0(A1,D1.W),A3
Move.B (A3)+,BPBYTESPERROW*4(A2)
Move.B (A3)+,BPBYTESPERROW*3(A2)
Move.B (A3)+,BPBYTESPERROW*2(A2)
Move.B (A3)+,BPBYTESPERROW*1(A2)
Move.B (A3),(A2)+
Bra.S .DrawStatus
.DoneStatus
.LeaveVB MoveM.L (Sp)+,D0-D7/A2-A6
Rts
;------------------------------
DrawWaveforms Move.L A4,-(Sp)
Lea Channel0(A4),A0
Lea YVert(PC),A3
MoveQ #4-1,D4
ChannelLoop Lea csc_Amplitudes(A0),A4
Move.L csc_AmplitudeTable(A0),A6
Move.L csc_ScreenBaseAddr(A0),A5
MoveQ #8-1,D6
Move.W csc_Frac(A0),D0
Move.W csc_SampleStep+2(A0),D1
Move.W csc_SampleStep(A0),D5
Move.L csc_CurSamplePtr(A0),A1
Move.L csc_SampleEnd(A0),D2
Lea csc_OldPositions(A0),A2
; One macro invocation for each pixel in the oscilloscope...
DRAWCHDOT 1
DRAWCHDOT 2
DRAWCHDOT 3
DRAWCHDOT 4
DRAWCHDOT 5
DRAWCHDOT 6
DRAWCHDOT 7
DRAWCHDOT 8
DRAWCHDOT 9
DRAWCHDOT 10
DRAWCHDOT 11
DRAWCHDOT 12
DRAWCHDOT 13
DRAWCHDOT 14
DRAWCHDOT 15
DRAWCHDOT 16
DRAWCHDOT 17
DRAWCHDOT 18
DRAWCHDOT 19
DRAWCHDOT 20
DRAWCHDOT 21
DRAWCHDOT 22
DRAWCHDOT 23
DRAWCHDOT 24
DRAWCHDOT 25
DRAWCHDOT 26
DRAWCHDOT 27
DRAWCHDOT 28
DRAWCHDOT 29
DRAWCHDOT 30
DRAWCHDOT 31
DRAWCHDOT 32
; Save parameters updated dynamically by the above macro invocations...
Move.L A1,csc_CurSamplePtr(A0)
Move.L D2,csc_SampleEnd(A0)
Move.W D0,csc_Frac(A0)
; Next audio channel oscilloscope...
Lea csc_SIZEOF(A0),A0
DBra D4,ChannelLoop
Move.L (Sp)+,A4
Rts
;------------------------------
; This routine renders the left and right total output oscilloscopes.
; It also calculates left and right output "voltage" for the peak meter
; displays...
DrawLeftRight Lea Channel0(A4),A0
Lea csc_Amplitudes(A0),A0
Lea Channel3(A4),A1
Lea csc_Amplitudes(A1),A1
Lea OldLeftAmplitudes(A4),A2
Move.L LeftScreenAddrP0(A4),A3
Move.L LeftScreenAddrP1(A4),A6
Lea YVert(PC),A5
MoveQ #-2,D0 ;Used as mask ($FFFE)
MoveQ #8-1,D3
; One macro invocation for each pixel in the oscilloscope...
DRAWLRDOT 1
DRAWLRDOT 2
DRAWLRDOT 3
DRAWLRDOT 4
DRAWLRDOT 5
DRAWLRDOT 6
DRAWLRDOT 7
DRAWLRDOT 8
DRAWLRDOT 9
DRAWLRDOT 10
DRAWLRDOT 11
DRAWLRDOT 12
DRAWLRDOT 13
DRAWLRDOT 14
DRAWLRDOT 15
DRAWLRDOT 16
DRAWLRDOT 17
DRAWLRDOT 18
DRAWLRDOT 19
DRAWLRDOT 20
DRAWLRDOT 21
DRAWLRDOT 22
DRAWLRDOT 23
DRAWLRDOT 24
DRAWLRDOT 25
DRAWLRDOT 26
DRAWLRDOT 27
DRAWLRDOT 28
DRAWLRDOT 29
DRAWLRDOT 30
DRAWLRDOT 31
DRAWLRDOT 32
DrawRight Lea Channel1(A4),A0
Lea csc_Amplitudes(A0),A0
Lea Channel2(A4),A1
Lea csc_Amplitudes(A1),A1
Lea OldRightAmplitudes(A4),A2
Move.L RightScreenAddrP0(A4),A3
Move.L RightScreenAddrP1(A4),A6
MoveQ #8-1,D3
; One macro invocation for each pixel in the oscilloscope...
DRAWLRDOT 1
DRAWLRDOT 2
DRAWLRDOT 3
DRAWLRDOT 4
DRAWLRDOT 5
DRAWLRDOT 6
DRAWLRDOT 7
DRAWLRDOT 8
DRAWLRDOT 9
DRAWLRDOT 10
DRAWLRDOT 11
DRAWLRDOT 12
DRAWLRDOT 13
DRAWLRDOT 14
DRAWLRDOT 15
DRAWLRDOT 16
DRAWLRDOT 17
DRAWLRDOT 18
DRAWLRDOT 19
DRAWLRDOT 20
DRAWLRDOT 21
DRAWLRDOT 22
DRAWLRDOT 23
DRAWLRDOT 24
DRAWLRDOT 25
DRAWLRDOT 26
DRAWLRDOT 27
DRAWLRDOT 28
DRAWLRDOT 29
DRAWLRDOT 30
DRAWLRDOT 31
DRAWLRDOT 32
; Before leaving, Compute the left and right output levels for the peak
; meter displays. I tried several different algorithms here in an attempt
; to more accurately simulate real peak meter output (I didn't just know how
; to do it because I'm not an audio engineer). The algorithm below is the
; one I finally settled on.
;
; The way it works: Since (to the best of my knowledge) a real peak meter
; displays output voltage in decibels, the algorithm below treats each output
; sample for a scope as a "voltage level". A sample value of 0 means "0 volts".
; Now, the left output level for the current frame is determined by first
; taking the 32 (or NDOTS) output samples from the channel 0 scope, obtaining
; the absolute value for each sample, and averaging them together. This
; effectively yields the average sustained "voltage" for the current frame for
; audio channel #0's output. The same calcs are then done for channel #3 (the
; other left output channel). Once this is done, the two resulting "voltage"
; averages are compared, and the higher of the two values is used.
;
; The same thing is done with channels #1 and #2 for the right output.
;
; In some cases there is a loss of accuracy (because not all samples played
; for the current frame are considered; only the samples that are actually
; displayed on the screen. The number of samples played per frame is typically
; a lot higher than the number of samples displayed per frame).
;
; Despite this occasional inaccuracy, I have found that the left/right peak
; meters in LScope now behave surprisingly like real peak meters. In fact, I
; have compared LScope's peak meter display to the peak meters on my tape
; deck, and I was quite surprised at just how accurate LScope's peak meters
; are. The only problem is, LScope is stuck at one "recording level" whereas
; I can change the level on my tape deck. Oh well...
Lea Channel0+csc_Amplitudes(A4),A0
Lea Channel3+csc_Amplitudes(A4),A1
Lea Channel1+csc_Amplitudes(A4),A2
Lea Channel2+csc_Amplitudes(A4),A3
MoveQ #0,D1
MoveQ #0,D2
MoveQ #0,D3
MoveQ #0,D4
MoveQ #0,D5
MoveQ #NDOTS-1,D0
.Loop Move.B (A0)+,D1 ;Get "voltage" output for channel #1.
Bpl.S .1
Neg.B D1 ;Make it positive (i.e. distance from "0 volts").
.1 Add.W D1,D2 ;Accumulate channel #1 "voltage" output.
Move.B (A1)+,D1 ;Do same for channel #2.
Bpl.S .2
Neg.B D1
.2 Add.W D1,D3
Move.B (A2)+,D1 ;Do same for channel #0.
Bpl.S .3
Neg.B D1
.3 Add.W D1,D4
Move.B (A3)+,D1 ;Do same for channel #3.
Bpl.S .4
Neg.B D1
.4 Add.W D1,D5
DBra D0,.Loop ;Loop for NDOTS samples from each channel.
Cmp.W D2,D3 ;Is channel #3 output higher than channel #0?
Ble.S .5 ;Nope.
Exg D2,D3 ;Yes, I want highest left output in D2.
.5 Cmp.W D4,D5 ;Is channel #2 output higher than channel #1?
Ble.S .6 ;Nope.
Exg D4,D5 ;Yes, I want highest right output in D4.
.6 Add.W D3,D2
Add.W D5,D4
Lsr.W #1,D2
Lsr.W #1,D4
Lsr.W #5,D2 ;Obtain average left output.
Lsr.W #5,D4 ;Obtain average right output.
Move.B D2,LeftOutput(A4) ;Store left output for this frame.
Move.B D4,RightOutput(A4) ;Store right output for this frame.
Rts
;------------------------------
; LScopeVBlank routine. This is the vertical blank service routine for the
; L-Scope program. It simply signals the main code each time the VBlank occurs.
; It also notifies the main code whenever the VBlank frequency changes (also
; via signalling).
;
; Input: A1 = Pointer to L-Scope variables (supplied by operating system).
;
LScopeVBlank MoveM.L D1/A0-A1/A4/A6,-(Sp)
Move.L A1,A4 ;A4 is my sacred address register. :)
LOADLIB SYS
Bsr CalcVBlankFrequency
Move.B D0,D1
MoveQ #0,D0
Cmp.B CurVBFreq(A4),D1
Beq .NoVBChange
Move.B D1,CurVBFreq(A4)
Move.B VBSigBit(A4),D1
BSet D1,D0
.NoVBChange Move.B RedrawSigBit(A4),D1
BSet D1,D0
Move.L LScopeTaskPtr(A4),A1
SYS Signal
MoveM.L (Sp)+,D1/A0-A1/A4/A6
MoveQ #0,D0
Rts
;------------------------------
; This routine prints a three digit numerical string to the screen. It's
; pretty damn fast (at some expense of memory, of course), considering it
; converts a byte value into a three digit decimal string AND renders it
; to the screen in only 368 cycles. If you call the routine at the "Print2Digits"
; entry point, it will make a 2 digit decimal string and render the string all in
; only 248 cycles. Yowsa! Note that I could have improved the speed even more
; by making the HunsLookup, TensLookup, and OnesLookup tables into longword tables
; holding pointers directly to the character images themselves, but the increase
; in speed would be negligible compared to the increase in memory requirements
; that would be necessary to pull it off. Know when to draw the line, I always
; say (FYI, I'd only drop about 42 cycles from the speed of this routine by doing
; such a modification, but the lookup tables would grow from 768 bytes to 3072).
; I could also save 34 cycles (the overhead of a BSR/RTS instruction pair) by
; repeating this whole routine everywhere I call it, but then the listing would
; start gettin' a wee bit long and tedious, so I figured, "...Not!". =)
;
; Input: D0.W = Byte value to be converted and displayed.
; A0 = Pointer to CharLookup table.
; A1 = Pointer to dest screen area to draw digits.
;
;Cycles (68000)
PrintByte MoveQ #0,D1 ; 4 ;Make sure D1 is clear.
Lea HunsLookup(PC),A3 ; 8 ;Get ptr to hundreds digits table.
Move.B 0(A3,D0.W),D1 ; 14 ;Get the hundreds digit index.
Move.L 0(A0,D1.W),A3 ; 18 ;Get pointer to char image.
Move.B (A3)+,BPBYTESPERROW*4(A1) ; 16 ;Draw 5th line of char.
Move.B (A3)+,BPBYTESPERROW*3(A1) ; 16 ;Draw 4th line.
Move.B (A3)+,BPBYTESPERROW*2(A1) ; 16 ;Draw 3rd line.
Move.B (A3)+,BPBYTESPERROW*1(A1) ; 16 ;Draw 2nd line.
Move.B (A3),(A1)+ ; 12 ;Draw 1st. next char cell.
Print2Digits MoveQ #0,D1 ; 4 ;Clear D1 for this entry point.
Lea TensLookup(PC),A3 ; 8 ;Get ptr to tens digit table.
Move.B 0(A3,D0.W),D1 ; 14 ;Get tens digit index.
Move.L 0(A0,D1.W),A3 ; 18 ;Get ptr to char image.
Move.B (A3)+,BPBYTESPERROW*4(A1) ; 16 ;Draw 5th line of char.
Move.B (A3)+,BPBYTESPERROW*3(A1) ; 16 ;Draw 4th line.
Move.B (A3)+,BPBYTESPERROW*2(A1) ; 16 ;Draw 3rd line.
Move.B (A3)+,BPBYTESPERROW*1(A1) ; 16 ;Draw 2nd line.
Move.B (A3),(A1)+ ; 12 ;Draw 1st, next char cell.
Move.B OnesLookup(PC,D0.W),D1 ; 18 ;Get ones digit index.
Move.L 0(A0,D1.W),A3 ; 18 ;Get ptr to char image.
Move.B (A3)+,BPBYTESPERROW*4(A1) ; 16 ;Draw 5th line of char.
Move.B (A3)+,BPBYTESPERROW*3(A1) ; 16 ;Draw 4th line.
Move.B (A3)+,BPBYTESPERROW*2(A1) ; 16 ;Draw 3rd line.
Move.B (A3)+,BPBYTESPERROW*1(A1) ; 16 ;Draw 2nd line.
Move.B (A3),(A1) ; 12 ;Draw 1st line. All done!
Rts ; 16 ;An RTS! Grrrr!
;---
;368 cycles total
;------------------------------
; This routine prints "---" at the screen area pointed at by A1. It is used
; for values that are meaningless under certain circumstances (like the
; number of patterns in a module when there is no module loaded, for example).
; I unwound it a bit to make it faster, since the loop code would be quite small,
; and the overhead for 3 loop iterations amounts to an increase of 38 cycles.
; Unwinding it didn't cost me much memory, so I figured, "What the hell...".
; This routine takes 188 cycles to render "---" at the screen dest in A1.
; (It would only take 52 cycles if I didn't have to erase the whole character
; cell).
;
; Input: A1 = Pointer to screen dest area, which will get trashed.
; D7 = 0
;
;Cycles;(68000)
PrintNA MoveQ #$7E,D0 ; 4 ;Image of "-" sign.
Move.B D7,BPBYTESPERROW*4(A1) ; 12 ;Erase 5th line of cell.
Move.B D7,BPBYTESPERROW*3(A1) ; 12 ;Erase 4th line.
Move.B D0,BPBYTESPERROW*2(A1) ; 12 ;Draw "-" to 3rd line.
Move.B D7,BPBYTESPERROW*1(A1) ; 12 ;Erase 2nd line.
Move.B D7,(A1)+ ; 8 ;Erase 1st, next char cell.
Move.B D7,BPBYTESPERROW*4(A1) ; 12 ;Erase 5th line of cell.
Move.B D7,BPBYTESPERROW*3(A1) ; 12 ;Erase 4th line.
Move.B D0,BPBYTESPERROW*2(A1) ; 12 ;Draw "-" to 3rd line.
Move.B D7,BPBYTESPERROW*1(A1) ; 12 ;Erase 2nd line.
Move.B D7,(A1)+ ; 8 ;Erase 1st, next char cell.
Move.B D7,BPBYTESPERROW*4(A1) ; 12 ;Erase 5th line of cell.
Move.B D7,BPBYTESPERROW*3(A1) ; 12 ;Erase 4th line.
Move.B D0,BPBYTESPERROW*2(A1) ; 12 ;Draw "-" to 3rd line.
Move.B D7,BPBYTESPERROW*1(A1) ; 12 ;Erase 2nd line.
Move.B D7,(A1) ; 8 ;Erase 1st line.
Rts ; 16 ;All done.
;---
;188 cycles total.
;---------------------------------------------------------------
; I hate to do this, but, as Chris Crawford once said: "Damn! I need a REALLY
; fast binary to decimal conversion routine!" ( Hey, if Chris Crawford can do
; it, then so can I. :). These three tables are used by the PrintByte routine.
; Each table is indexed by the byte value to be converted to a decimal string
; and rendered, and each table contains offset values into the CharLookup table
; for acquiring a ptr to the character image to draw (The values in this table
; are pre-shifted left twice to create a longword index from the ASCII digits
; "0" to "9" (i.e. $30 ("0") shifted left twice (multiplied by four) yields
; $C0, "1" yields $C1, etc). Fortunately for me, the shifted offsets still fit
; in a byte, but just barely.
;
; Seriously, I got the idea for using these tables from Chris Crawford. In fact,
; I saw a similar binary to decimal conversion routine (using tables like those
; below) in Mr. Crawford's source code for Eastern Front 1941 for the 8-bit Atari
; many many years ago. Chris Crawford was my programming idol until he sold out
; and moved to IBM-Land. Oh Chris, why hast thou forsaken me...
OnesLookup
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4
Dc.B $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC,$E0,$E4,$C0,$C4,$C8,$CC,$D0,$D4
TensLookup
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
Dc.B $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4
Dc.B $D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC
Dc.B $E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
Dc.B $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4,$D4
Dc.B $D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$D8,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC,$DC
Dc.B $E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E0,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4,$E4
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
Dc.B $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0,$D4,$D4,$D4,$D4,$D4,$D4
HunsLookup
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0
Dc.B $C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0,$C0
Dc.B $C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4,$C4
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8
Dc.B $C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8,$C8
;------------------------------
; Scan line delta table. Holds screen memory offsets of n scan lines...
; This lookup table flips the delta Y on the X axis so that negative
; amplitudes are displayed in the bottom half of the scope and positive
; amplitudes in the upper half...
Dc.W BPBYTESPERROW*16
Dc.W BPBYTESPERROW*15
Dc.W BPBYTESPERROW*14
Dc.W BPBYTESPERROW*13
Dc.W BPBYTESPERROW*12
Dc.W BPBYTESPERROW*11
Dc.W BPBYTESPERROW*10
Dc.W BPBYTESPERROW*9
Dc.W BPBYTESPERROW*8
Dc.W BPBYTESPERROW*7
Dc.W BPBYTESPERROW*6
Dc.W BPBYTESPERROW*5
Dc.W BPBYTESPERROW*4
Dc.W BPBYTESPERROW*3
Dc.W BPBYTESPERROW*2
Dc.W BPBYTESPERROW*1
Dc.W BPBYTESPERROW*0
YVert Dc.W BPBYTESPERROW*-1
Dc.W BPBYTESPERROW*-2
Dc.W BPBYTESPERROW*-3
Dc.W BPBYTESPERROW*-4
Dc.W BPBYTESPERROW*-5
Dc.W BPBYTESPERROW*-6
Dc.W BPBYTESPERROW*-7
Dc.W BPBYTESPERROW*-8
Dc.W BPBYTESPERROW*-9
Dc.W BPBYTESPERROW*-10
Dc.W BPBYTESPERROW*-11
Dc.W BPBYTESPERROW*-12
Dc.W BPBYTESPERROW*-13
Dc.W BPBYTESPERROW*-14
Dc.W BPBYTESPERROW*-15
Dc.W BPBYTESPERROW*-16
Dc.W BPBYTESPERROW*-17
;------------------------------
; Base clock constant lookup table for North American Amigas.
; Covers base constants for VBlank frequencies in the
; range 30..80Hz.
;
; Formula per longword element i:
;
; Table[i] = TRUNC((((3579545/(i+30))/2)*256)+0.5)
BaseNAmerica Dc.L $00E90B15 ;30Hz
Dc.L $00E18699 ;31Hz
Dc.L $00DA7A64 ;32Hz
Dc.L $00D3DB88 ;33Hz
Dc.L $00CDA05E ;34Hz
Dc.L $00C7C05B ;35Hz
Dc.L $00C233E7 ;36Hz
Dc.L $00BCF436 ;37Hz
Dc.L $00B7FB47 ;38Hz
Dc.L $00B3439A ;39Hz
Dc.L $00AEC850 ;40Hz
Dc.L $00AA84FD ;41Hz
Dc.L $00A675A2 ;42Hz
Dc.L $00A2969E ;43Hz
Dc.L $009EE4A6 ;44Hz
Dc.L $009B5CB9 ;45Hz
Dc.L $0097FC19 ;46Hz
Dc.L $0094C044 ;47Hz
Dc.L $0091A6ED ;48Hz
Dc.L $008EADF8 ;49Hz
Dc.L $008BD373 ;50Hz
Dc.L $00891594 ;51Hz
Dc.L $008672B4 ;52Hz
Dc.L $0083E94B ;53Hz
Dc.L $008177EF ;54Hz
Dc.L $007F1D51 ;55Hz
Dc.L $007CD839 ;56Hz
Dc.L $007AA784 ;57Hz
Dc.L $00788A26 ;58Hz
Dc.L $00767F21 ;59Hz
Dc.L $0074858B ;60Hz
Dc.L $00729C88 ;61Hz
Dc.L $0070C34C ;62Hz
Dc.L $006EF916 ;63Hz
Dc.L $006D3D32 ;64Hz
Dc.L $006B8EF6 ;65Hz
Dc.L $0069EDC4 ;66Hz
Dc.L $00685906 ;67Hz
Dc.L $0066D02F ;68Hz
Dc.L $006552BB ;69Hz
Dc.L $0063E02E ;70Hz
Dc.L $00627810 ;71Hz
Dc.L $006119F4 ;72Hz
Dc.L $005FC56E ;73Hz
Dc.L $005E7A1D ;74Hz
Dc.L $005D37A2 ;75Hz
Dc.L $005BFDA3 ;76Hz
Dc.L $005ACBCC ;77Hz
Dc.L $0059A1CD ;78Hz
Dc.L $00587F59 ;79Hz
Dc.L $00576428 ;80Hz
;------------------------------
; Base clock constant lookup table for European Amigas.
; Covers base frequency constants for VBlank frequencies in the
; range 30..80.
;
; Formula per longword element i:
;
; Table[i] = TRUNC((((3546895/(i+30))/2)*256)+0.5)
BaseEurope Dc.L $00E6EAEB ;30Hz
Dc.L $00DF77FC ;31Hz
Dc.L $00D87C3C ;32Hz
Dc.L $00D1ECD5 ;33Hz
Dc.L $00CBC038 ;34Hz
Dc.L $00C5EDEE ;35Hz
Dc.L $00C06E6E ;36Hz
Dc.L $00BB3B03 ;37Hz
Dc.L $00B64DAC ;38Hz
Dc.L $00B1A103 ;39Hz
Dc.L $00AD3030 ;40Hz
Dc.L $00A8F6D1 ;41Hz
Dc.L $00A4F0F1 ;42Hz
Dc.L $00A11AF7 ;43Hz
Dc.L $009D71A0 ;44Hz
Dc.L $0099F1F2 ;45Hz
Dc.L $00969935 ;46Hz
Dc.L $009364ED ;47Hz
Dc.L $009052D3 ;48Hz
Dc.L $008D60CE ;49Hz
Dc.L $008A8CF3 ;50Hz
Dc.L $0087D57B ;51Hz
Dc.L $008538C2 ;52Hz
Dc.L $0082B546 ;53Hz
Dc.L $0080499F ;54Hz
Dc.L $007DF480 ;55Hz
Dc.L $007BB4B5 ;56Hz
Dc.L $0079891D ;57Hz
Dc.L $007770AE ;58Hz
Dc.L $00756A6F ;59Hz
Dc.L $00737575 ;60Hz
Dc.L $007190E9 ;61Hz
Dc.L $006FBBFE ;62Hz
Dc.L $006DF5F6 ;63Hz
Dc.L $006C3E1E ;64Hz
Dc.L $006A93CF ;65Hz
Dc.L $0068F66B ;66Hz
Dc.L $0067655E ;67Hz
Dc.L $0065E01C ;68Hz
Dc.L $00646623 ;69Hz
Dc.L $0062F6F7 ;70Hz
Dc.L $00619222 ;71Hz
Dc.L $00603737 ;72Hz
Dc.L $005EE5CD ;73Hz
Dc.L $005D9D82 ;74Hz
Dc.L $005C5DF7 ;75Hz
Dc.L $005B26D6 ;76Hz
Dc.L $0059F7C9 ;77Hz
Dc.L $0058D082 ;78Hz
Dc.L $0057B0B4 ;79Hz
Dc.L $00569818 ;80Hz
;------------------------------
ScanlineSteps Dc.L $0000,$0000,$1000,$1000,$1000,$2000,$2000,$2000
Dc.L $2000,$3000,$3000,$3000,$3000,$4000,$4000,$4000
Dc.L $4000,$5000,$5000,$5000,$5000,$6000,$6000,$6000
Dc.L $6000,$7000,$7000,$7000,$7000,$8000,$8000,$8000
Dc.L $8000,$9000,$9000,$9000,$9000,$A000,$A000,$A000
Dc.L $A000,$B000,$B000,$B000,$B000,$C000,$C000,$C000
Dc.L $C000,$D000,$D000,$D000,$D000,$E000,$E000,$E000
Dc.L $E000,$F000,$F000,$F000,$F000,$10000,$10000,$10000
Dc.L $10000
;------------------------------
ScanlineStarts Dc.B 0,0,-1,-1,-1,-2,-2,-2,-2,-3,-3,-3,-3,-4,-4,-4,-4
Dc.B -5,-5,-5,-5,-6,-6,-6,-6,-7,-7,-7,-7,-8,-8,-8,-8
Dc.B -9,-9,-9,-9,-10,-10,-10,-10,-11,-11,-11,-11
Dc.B -12,-12,-12,-12,-13,-13,-13,-13,-14,-14,-14,-14
Dc.B -15,-15,-15,-15,-16,-16,-16,-16
Even
;------------------------------
BlankSample Dc.L 0 ;Used when no sample is playing in a channel.
;------------------------------
; This is the NEWSCREEN structure for creating the screen that L-Scope uses...
MyNewScreen Dc.W 0 ;WORD ns_LeftEdge
Dc.W 0 ;WORD ns_TopEdge
Dc.W 320 ;WORD ns_Width
Dc.W 210 ;WORD ns_Height
Dc.W 3 ;WORD ns_Depth
Dc.B 1 ;BYTE ns_DetailPen
Dc.B 0 ;BYTE ns_BlockPen
Dc.W 0 ;WORD ns_ViewModes
Dc.W CUSTOMSCREEN|SCREENBEHIND ;WORD ns_Type
Dc.L TopazFontAttr ;APTR ns_Font
Dc.L MyTitle ;APTR ns_DefaultTitle
Dc.L 0 ;APTR ns_Gadgets
Dc.L 0 ;APTR ns_CustomBitMap
; This is the NEWWINDOW structure for creating the window that L-Scope uses...
MyNewWindow Dc.W 0 ;WORD nw_LeftEdge
Dc.W 10 ;WORD nw_TopEdge
Dc.W 320 ;WORD nw_Width
Dc.W 200 ;WORD nw_Height
Dc.B 1 ;BYTE nw_DetailPen
Dc.B 0 ;BYTE nw_BlockPen
Dc.L MYIDCMP ;LONG nw_IDCMPFlags
Dc.L MYWFLAGS ;LONG nw_Flags
Dc.L 0 ;APTR nw_FirstGadget
Dc.L 0 ;APTR nw_CheckMark
Dc.L 0 ;APTR nw_Title
NWScreen Dc.L 0 ;APTR nw_Screen (not good for pure code!)
Dc.L 0 ;APTR nw_BitMap
Dc.W 0 ;WORD nw_MinWidth
Dc.W 0 ;WORD nw_MinHeight
Dc.W 0 ;WORD nw_MaxWidth
Dc.W 0 ;WORD nw_MaxHeight
Dc.W CUSTOMSCREEN ;WORD nw_Type
;LABEL nw_SIZE
; This is the text attribute structure for use with my screen...
TopazFontAttr Dc.L TopazName
Dc.W TOPAZ_EIGHTY
Dc.B FS_NORMAL
Dc.B FPF_ROMFONT
TopazName Dc.B "topaz.font",0
Even
; The following table is used for looking up screen raster line positions at
; which to draw/erase pieces of the channel volume bars (typically the topmost
; line of the blue part of the bar)...
ChannelOffsets Dc.W COBASE+(63*BPBYTESPERROW)
Dc.W COBASE+(62*BPBYTESPERROW)
Dc.W COBASE+(61*BPBYTESPERROW)
Dc.W COBASE+(60*BPBYTESPERROW)
Dc.W COBASE+(59*BPBYTESPERROW)
Dc.W COBASE+(58*BPBYTESPERROW)
Dc.W COBASE+(57*BPBYTESPERROW)
Dc.W COBASE+(56*BPBYTESPERROW)
Dc.W COBASE+(55*BPBYTESPERROW)
Dc.W COBASE+(54*BPBYTESPERROW)
Dc.W COBASE+(53*BPBYTESPERROW)
Dc.W COBASE+(52*BPBYTESPERROW)
Dc.W COBASE+(51*BPBYTESPERROW)
Dc.W COBASE+(50*BPBYTESPERROW)
Dc.W COBASE+(49*BPBYTESPERROW)
Dc.W COBASE+(48*BPBYTESPERROW)
Dc.W COBASE+(47*BPBYTESPERROW)
Dc.W COBASE+(46*BPBYTESPERROW)
Dc.W COBASE+(45*BPBYTESPERROW)
Dc.W COBASE+(44*BPBYTESPERROW)
Dc.W COBASE+(43*BPBYTESPERROW)
Dc.W COBASE+(42*BPBYTESPERROW)
Dc.W COBASE+(41*BPBYTESPERROW)
Dc.W COBASE+(40*BPBYTESPERROW)
Dc.W COBASE+(39*BPBYTESPERROW)
Dc.W COBASE+(38*BPBYTESPERROW)
Dc.W COBASE+(37*BPBYTESPERROW)
Dc.W COBASE+(36*BPBYTESPERROW)
Dc.W COBASE+(35*BPBYTESPERROW)
Dc.W COBASE+(34*BPBYTESPERROW)
Dc.W COBASE+(33*BPBYTESPERROW)
Dc.W COBASE+(32*BPBYTESPERROW)
Dc.W COBASE+(31*BPBYTESPERROW)
Dc.W COBASE+(30*BPBYTESPERROW)
Dc.W COBASE+(29*BPBYTESPERROW)
Dc.W COBASE+(28*BPBYTESPERROW)
Dc.W COBASE+(27*BPBYTESPERROW)
Dc.W COBASE+(26*BPBYTESPERROW)
Dc.W COBASE+(25*BPBYTESPERROW)
Dc.W COBASE+(24*BPBYTESPERROW)
Dc.W COBASE+(23*BPBYTESPERROW)
Dc.W COBASE+(22*BPBYTESPERROW)
Dc.W COBASE+(21*BPBYTESPERROW)
Dc.W COBASE+(20*BPBYTESPERROW)
Dc.W COBASE+(19*BPBYTESPERROW)
Dc.W COBASE+(18*BPBYTESPERROW)
Dc.W COBASE+(17*BPBYTESPERROW)
Dc.W COBASE+(16*BPBYTESPERROW)
Dc.W COBASE+(15*BPBYTESPERROW)
Dc.W COBASE+(14*BPBYTESPERROW)
Dc.W COBASE+(13*BPBYTESPERROW)
Dc.W COBASE+(12*BPBYTESPERROW)
Dc.W COBASE+(11*BPBYTESPERROW)
Dc.W COBASE+(10*BPBYTESPERROW)
Dc.W COBASE+(09*BPBYTESPERROW)
Dc.W COBASE+(08*BPBYTESPERROW)
Dc.W COBASE+(07*BPBYTESPERROW)
Dc.W COBASE+(06*BPBYTESPERROW)
Dc.W COBASE+(05*BPBYTESPERROW)
Dc.W COBASE+(04*BPBYTESPERROW)
Dc.W COBASE+(03*BPBYTESPERROW)
Dc.W COBASE+(02*BPBYTESPERROW)
Dc.W COBASE+(01*BPBYTESPERROW)
Dc.W COBASE+(00*BPBYTESPERROW)
; The values in this table are actually pointers to the last row of the
; corresponding character image. The reason I do this is because I use
; pre-decrement on the address register that points to the character
; image (I draw it from the bottom up), and so the addr register must point
; to the last byte of the character image that I want to render...
CharLookup
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C01,C02,C03,C04,C05,C06,C07,C08,C09,C10,C11,C12,C13,C14,C15,C16
Dc.L C17,C18,C19,C20,C21,C22,C23,C24,C25,C26,C27,C28,C29,C30,C31,C32
Dc.L C33,C34,C35,C36,C37,C38,C39,C40,C41,C42,C43,C44,C45,C46,C47,C48
Dc.L C49,C50,C51,C52,C53,C54,C55,C56,C57,C58,C59,C60,C61,C62,C63,C64
Dc.L C65,C34,C35,C36,C37,C38,C39,C40,C41,C42,C43,C44,C45,C46,C47,C48
Dc.L C49,C50,C51,C52,C53,C54,C55,C56,C57,C58,C59,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
Dc.L C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66,C66
; Pointer table for looking up the address of the status text that I want to
; print. I use the top 3 bits of the low byte of dtl_Flags as my index (shifted
; down to bits 0..2 of course)...
StatusPtrs Dc.L NoModText
Dc.L IdleText
Dc.L PlayingText
Dc.L PausedText
Dc.L StoppedText
Dc.L PSDoneText
; The 8-color palette for my 3-plane display...
PicPalette Dc.W $444,$777,$000,$AAA,$66F,$F00,$0F0,$FF0
; Library name strings for OpenLibrary...
IntName Dc.B "intuition.library",0
GfxName Dc.B "graphics.library",0
DTLName DTLIBNAME
; The name of my vertical blank routine (and message port)...
ProgName Dc.B "L-Scope",0
VBlankName Dc.B "L-Scope VBL",0
; The text for my screen's title bar...
MyTitle Dc.B "L-Scope 2.0 by D. E. Schebek",0
;==============================
DATA
Even
; Here are the images of all the characters I need to render from time to time.
; Each character is one byte wide by five bytes tall (occupying...uh... 5 bytes
; each!). The first character is the space character, and they proceed in ASCII
; order all the way up to the reverse apostrophe character (including an "illegal
; char" character image at the very end). You may notice that all of these images
; are upside-down. Well, this is because they are drawn from the bottom up (so
; that the last line can automatically advance to the next character cell to the
; right at the same time), but I want to use post-increment to draw the chars,
; since a post-increment MOVE.B is 2 cycles faster than a pre-decrement MOVE.B.
; Like I said before, all these little cycles add up after a while...
C01 Dc.B %00000000 ;Space character.
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
C02 Dc.B %00011000 ;' ! '
Dc.B %00000000
Dc.B %00011000
Dc.B %00011000
Dc.B %00011000
C03 Dc.B %00000000 ;' " '
Dc.B %00000000
Dc.B %00000000
Dc.B %01101100
Dc.B %01101100
C04 Dc.B %01101100 ;' # '
Dc.B %11111110
Dc.B %01101100
Dc.B %11111110
Dc.B %01101100
C05 Dc.B %01111100 ;' $ '
Dc.B %00010110
Dc.B %01111000
Dc.B %11010000
Dc.B %01111100
C06 Dc.B %01100110 ;' % '
Dc.B %00110110
Dc.B %00011000
Dc.B %01101100
Dc.B %01100110
C07 Dc.B %00111100 ;' & '
Dc.B %01101110
Dc.B %00110000
Dc.B %00011000
Dc.B %00011000
C08 Dc.B %00000000 ;' ' '
Dc.B %00000000
Dc.B %00000000
Dc.B %00011000
Dc.B %00011000
C09 Dc.B %00000110 ;' ( '
Dc.B %00001100
Dc.B %00001100
Dc.B %00001100
Dc.B %00000110
C10 Dc.B %01100000 ;' ) '
Dc.B %00110000
Dc.B %00110000
Dc.B %00110000
Dc.B %01100000
C11 Dc.B %01010100 ;' * '
Dc.B %00111000
Dc.B %11111110
Dc.B %00111000
Dc.B %01010100
C12 Dc.B %00011000 ;' + '
Dc.B %00011000
Dc.B %01111110
Dc.B %00011000
Dc.B %00011000
C13 Dc.B %11000000 ;' , '
Dc.B %01100000
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
C14 Dc.B %00000000 ;' - '
Dc.B %00000000
Dc.B %01111110
Dc.B %00000000
Dc.B %00000000
C15 Dc.B %00110000 ;' . '
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
C16 Dc.B %01100000 ;' / '
Dc.B %00110000
Dc.B %00011000
Dc.B %00001100
Dc.B %00000110
C17 Dc.B %00111100 ;' 0 '
Dc.B %01100110
Dc.B %01110110
Dc.B %01101110
Dc.B %00111100
C18 Dc.B %00001100 ;' 1 '
Dc.B %00001100
Dc.B %00001100
Dc.B %00111100
Dc.B %00011100
C19 Dc.B %01111110 ;' 2 '
Dc.B %00110000
Dc.B %00001100
Dc.B %00000110
Dc.B %01111100
C20 Dc.B %01111100 ;' 3 '
Dc.B %00000110
Dc.B %00111100
Dc.B %00000110
Dc.B %01111100
C21 Dc.B %00001100 ;' 4 '
Dc.B %11111110
Dc.B %01101100
Dc.B %00111100
Dc.B %00011100
C22 Dc.B %01111100 ;' 5 '
Dc.B %00000110
Dc.B %01111100
Dc.B %01100000
Dc.B %01111110
C23 Dc.B %00111100 ;' 6 '
Dc.B %01100110
Dc.B %01111100
Dc.B %01100000
Dc.B %00111000
C24 Dc.B %00011000 ;' 7 '
Dc.B %00011000
Dc.B %00001100
Dc.B %00000110
Dc.B %01111110
C25 Dc.B %00111100 ;' 8 '
Dc.B %01100110
Dc.B %00111100
Dc.B %01100110
Dc.B %00111100
C26 Dc.B %00011100 ;' 9 '
Dc.B %00000110
Dc.B %00111110
Dc.B %01100110
Dc.B %00111100
C27 Dc.B %00000000 ;' : '
Dc.B %00110000
Dc.B %00000000
Dc.B %00110000
Dc.B %00000000
C28 Dc.B %01100000 ;' ; '
Dc.B %00110000
Dc.B %00000000
Dc.B %00110000
Dc.B %00000000
C29 Dc.B %00011000 ;' < '
Dc.B %00110000
Dc.B %01100000
Dc.B %00110000
Dc.B %00011000
C30 Dc.B %00000000 ;' = '
Dc.B %01111110
Dc.B %00000000
Dc.B %01111110
Dc.B %00000000
C31 Dc.B %00011000 ;' > '
Dc.B %00001100
Dc.B %00000110
Dc.B %00001100
Dc.B %00011000
C32 Dc.B %00011000 ;' ? '
Dc.B %00000000
Dc.B %00011000
Dc.B %01001110
Dc.B %00111100
C33 Dc.B %00111110 ;' @ '
Dc.B %01100000
Dc.B %01101110
Dc.B %01100110
Dc.B %00111100
C34 Dc.B %01100110 ;' A '
Dc.B %01100110
Dc.B %01111110
Dc.B %01100110
Dc.B %00111100
C35 Dc.B %01111100 ;' B '
Dc.B %01100110
Dc.B %01111100
Dc.B %01100110
Dc.B %01111100
C36 Dc.B %00111100 ;' C '
Dc.B %01100110
Dc.B %01100000
Dc.B %01100110
Dc.B %00111100
C37 Dc.B %01111100 ;' D '
Dc.B %01100110
Dc.B %01100110
Dc.B %01100110
Dc.B %01111100
C38 Dc.B %01111110 ;' E '
Dc.B %01100000
Dc.B %01111100
Dc.B %01100000
Dc.B %01111110
C39 Dc.B %01100000 ;' F '
Dc.B %01100000
Dc.B %01111100
Dc.B %01100000
Dc.B %01111110
C40 Dc.B %00111110 ;' G '
Dc.B %01100110
Dc.B %01101110
Dc.B %01100000
Dc.B %00111110
C41 Dc.B %01100110 ;' H '
Dc.B %01100110
Dc.B %01111110
Dc.B %01100110
Dc.B %01100110
C42 Dc.B %00111100 ;' I '
Dc.B %00011000
Dc.B %00011000
Dc.B %00011000
Dc.B %00111100
C43 Dc.B %00111100 ;' J '
Dc.B %01100110
Dc.B %00000110
Dc.B %00000110
Dc.B %00000110
C44 Dc.B %01100110 ;' K '
Dc.B %01101100
Dc.B %01111000
Dc.B %01101100
Dc.B %01100110
C45 Dc.B %01111110 ;' L '
Dc.B %01100000
Dc.B %01100000
Dc.B %01100000
Dc.B %01100000
C46 Dc.B %01100110 ;' M '
Dc.B %01100110
Dc.B %01111110
Dc.B %01100110
Dc.B %01000010
C47 Dc.B %01100110 ;' N '
Dc.B %01101110
Dc.B %01111110
Dc.B %01110110
Dc.B %01100110
C48 Dc.B %00111100 ;' O '
Dc.B %01100110
Dc.B %01100110
Dc.B %01100110
Dc.B %00111100
C49 Dc.B %01100000 ;' P '
Dc.B %01100000
Dc.B %01111100
Dc.B %01100110
Dc.B %01111100
C50 Dc.B %00111111 ;' Q '
Dc.B %01110110
Dc.B %01101110
Dc.B %01100110
Dc.B %00111100
C51 Dc.B %01100110 ;' R '
Dc.B %01100110
Dc.B %01111100
Dc.B %01100110
Dc.B %01111100
C52 Dc.B %01111100 ;' S '
Dc.B %00000110
Dc.B %00111100
Dc.B %01100000
Dc.B %00111110
C53 Dc.B %00011000 ;' T '
Dc.B %00011000
Dc.B %00011000
Dc.B %00011000
Dc.B %01111110
C54 Dc.B %00111100 ;' U '
Dc.B %01100110
Dc.B %01100110
Dc.B %01100110
Dc.B %01100110
C55 Dc.B %00011000 ;' V '
Dc.B %00111100
Dc.B %01100110
Dc.B %01100110
Dc.B %01100110
C56 Dc.B %01000010 ;' W '
Dc.B %01100110
Dc.B %01111110
Dc.B %01100110
Dc.B %01100110
C57 Dc.B %01100110 ;' X '
Dc.B %00111100
Dc.B %00011000
Dc.B %00111100
Dc.B %01100110
C58 Dc.B %00011000 ;' Y '
Dc.B %00011000
Dc.B %00111100
Dc.B %01100110
Dc.B %01100110
C59 Dc.B %01111110 ;' Z '
Dc.B %00110000
Dc.B %00011000
Dc.B %00001100
Dc.B %01111110
C60 Dc.B %00001110 ;' [ '
Dc.B %00001100
Dc.B %00001100
Dc.B %00001100
Dc.B %00001110
C61 Dc.B %00000110 ;' / '
Dc.B %00001100
Dc.B %00011000
Dc.B %00110000
Dc.B %01100000
C62 Dc.B %01110000 ;' ] '
Dc.B %00110000
Dc.B %00110000
Dc.B %00110000
Dc.B %01110000
C63 Dc.B %00000000 ;' ^ '
Dc.B %00000000
Dc.B %11001100
Dc.B %01111000
Dc.B %00110000
C64 Dc.B %11111110 ;' _ '
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
Dc.B %00000000
C65 Dc.B %00000000 ;' ` '
Dc.B %00000000
Dc.B %00000000
Dc.B %00110000
Dc.B %01100000
C66 Dc.B %11111110 ;"Illegal char" character.
Dc.B %11000110
Dc.B %11000110
Dc.B %11000110
Dc.B %11111110
; These are the various status text strings. Each is twelve characters long, so
; I don't have to erase bits of strings left behind on the screen...
NoModText Dc.B " NO MODULE ",0
IdleText Dc.B " IDLE ",0
PlayingText Dc.B " ROCKIN'! ",0
PausedText Dc.B " PAUSED ",0
StoppedText Dc.B " STOPPED! ",0
PSDoneText Dc.B "PLAYSEQ DONE",0
Even
; These four parameters are required solely by the cback.o code. I'll be having
; nuttin' to do wit' 'em... :)
_BackGroundIO Dc.L 0 ;This stuff
_priority Dc.L 0 ;is only here
_stack Dc.L 4000 ;because I'm linking
_procname Dc.L ProgName ;with cback.o. Grrr!
;==============================
;
; Grrrr! cback.o wants to store to absolute memory locations, so I need
; this block storage segment (I'd prefer to have all my variables allocated
; as one block of memory and referenced off an address register. Grrrr!)
; One of these days I'm gonna hafta modify newcback.o and teach it about
; pure code (*Sigh*). Of course, I'm no angel either, since I did the same
; damn thing with NWScreen in my NewWindow structure. Blech...
BSS
Even
DOSBase Ds.L 1 ;I don't even NEED this variable. Grrrr!
End